概念检查器无法在 gcc 上编译,因为它 'has no linkage'

concept checker doesn't compile on gcc because it 'has no linkage'

我创建了一个基于 this question 的概念检查 class,其目的是确保给定的 class 具有名为 baseUnitConversionFactor 的静态成员函数。 class 编译并与 msvc2013 一起正常工作,但它不会在 gcc 4.9.2 上编译(使用 -std=c++14)并出现错误:

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>*);

我真的不知道那是什么意思,而且我更熟悉在视觉工作室(显然)更宽松的环境中编写模板。谁能帮我弄清楚我需要做什么来解决这个问题?

概念检查器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_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);
}

烦人的是,这个问题似乎是由 gtest 使用匿名名称空间的方式引起的。将 validUnit 声明移至测试夹具(称为 UnitsTest)并将 EXPECT 语句更改为使用夹具命名空间解决了该问题。

更新的夹具

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;
    };
};

更新测试

TEST_F(UnitsTest, conceptChecker)
{
    EXPECT_TRUE(has_baseUnitConversionFactor<UnitsTest::validUnit>::value);
}

在 C++11 和 C++14 中,pointer/reference 模板参数必须引用具有链接的实体(在 C++03 中,它们仅限于具有外部链接的实体)。本地 class 没有链接,它的成员函数也没有。

此限​​制已被 N4268 在 C++17 中删除,GCC trunk 声称已实现该论文,但显然不是链接部分。

回避这个问题不需要使用 &U::baseUnitConversionFactor 作为模板 non-type 参数。令人高兴的是,测试表达式 T::baseUnitConversionFactor() 是否有效且 returns 恰好 double 的一种更简单的方法是:

template <typename T, class=double>
struct has_baseUnitConversionFactor : std::false_type { };

template <typename T>
struct has_baseUnitConversionFactor<T, decltype(T::baseUnitConversionFactor())>
         : std::true_type { };

这确实取决于表达式 SFINAE(但是,原始表达式也是如此),所以我不确定它是否适用于 MSVC 2013。

要进行更一般的检查,您可能需要查看 std::experimental::is_detected_convertible。该 cppreference 页面有一个参考实现。