问题
I've created a concept checking class based on this question whose purpose is to make sure a given class has a static member function called baseUnitConversionFactor
. The class compiles and works fine with msvc2013, but it wont compile on gcc 4.9.2 (using -std=c++14) with the error:
error: ‘{anonymous}::UnitsTest_conceptChecker_Test::TestBody()::validUnit::baseUnitConversionFactor’ is not a valid template argument for type ‘double (*)()’ because ‘static double {anonymous}::UnitsTest_conceptChecker_Test::TestBody()::validUnit::baseUnitConversionFactor()’ has no linkage
static std::true_type test(tester<&U::baseUnitConversionFactor>*);
I don't really know what that means, and am much more familiar with writing templates in visual studios (obviously) much more permisive enviornment. Can anyone help figure out what I need to do to fix this?
Concept Checker Class
template <typename T>
struct has_baseUnitConversionFactor
{
template<double(*)()> struct tester;
template<typename U>
static std::true_type test(tester<&U::baseUnitConversionFactor>*);
template<typename U>
static std::false_type test(...);
static const bool value = decltype(test<T>(0))::value;
};
Test which I think causes the error
TEST_F(UnitsTest, conceptChecker)
{
struct validUnit
{
static inline double baseUnitConversionFactor() { return 0.0; }
typedef void unit_category;
typedef void base_unit_type;
};
EXPECT_TRUE(has_baseUnitConversionFactor<validUnit>::value);
}
回答1:
In C++11 and C++14, pointer/reference template arguments must refer to entities with linkage (in C++03, they were limited to entities with external linkage). A local class has no linkage, and neither do its member functions.
This restriction has been removed in C++17 by N4268, and GCC trunk claims to have implemented that paper, but apparently not the linkage part.
Sidestepping this issue requires not using &U::baseUnitConversionFactor
as a template non-type argument. Happily, a much simpler way to test that the expression T::baseUnitConversionFactor()
is valid and returns exactly double
is:
template <typename T, class=double>
struct has_baseUnitConversionFactor : std::false_type { };
template <typename T>
struct has_baseUnitConversionFactor<T, decltype(T::baseUnitConversionFactor())>
: std::true_type { };
This does depend on expression SFINAE (but then, so does the original), so I'm not sure if it will work on MSVC 2013.
For a more general check, you may want to look at std::experimental::is_detected_convertible. That cppreference page has a reference implementation.
回答2:
Annoyingly, the problem seems to be caused by the way gtest uses anonymous namespaces. Moving the validUnit
declaration into the test fixture (called UnitsTest) and changing the EXPECT statement to use the fixture namespace solved the issue.
Updated Fixture
class UnitsTest : public ::testing::Test {
protected:
UnitsTest()
{
}
virtual ~UnitsTest()
{
}
virtual void SetUp()
{
}
virtual void TearDown()
{
}
struct validUnit
{
static inline double baseUnitConversionFactor() { return 0.0; }
typedef void unit_category;
typedef void base_unit_type;
};
};
Updated Test
TEST_F(UnitsTest, conceptChecker)
{
EXPECT_TRUE(has_baseUnitConversionFactor<UnitsTest::validUnit>::value);
}
来源:https://stackoverflow.com/questions/34501232/concept-checker-doesnt-compile-on-gcc-because-it-has-no-linkage