【问题标题】:Undefined reference, template struct and constexpr static member未定义的引用、模板结构和 constexpr 静态成员
【发布时间】:2016-01-25 02:15:21
【问题描述】:

我遇到了模板结构的静态 constexpr 成员的一些问题。代码编译但我得到链接错误。这是我正在尝试做的事情:

template<int n>
struct Test {
    static constexpr auto invoke = make_tuple(2, "test", 3.4);
};

template<typename T>
void test(T&& t) {
    cout << t << endl;
}

int main() {
    test(get<0>(Test<2>::invoke));
    return 0;
}

我遇到了链接错误,所以我尝试了这个:

template<int n>
struct Test {
    static constexpr auto invoke = make_tuple(2, "test", 3.4);
};

// declare it outside the class
template<int n>
constexpr decltype(Test<n>::invoke) Test<n>::invoke;

template<typename T>
void test(T&& t) {
    cout << t << endl;
}

int main() {
    test(get<0>(Test<2>::invoke));
    return 0;
}

但是我得到了这个奇怪的错误:

error: redefinition of 'invoke' with a different type: 'const decltype(Test<n>::invoke)' vs 'const std::tuple<int, const char *, double>'

不同的类型?? 显然,非模板版本的效果很好:

struct Test {
    static constexpr auto invoke = make_tuple(2, "test", 3.4);
};

constexpr decltype(Test::invoke) Test::invoke;

template<typename T>
void test(T&& t) {
    cout << t << endl;
}

int main() {
    test(get<0>(Test::invoke));
    return 0;
}

如何使模板版本正常工作?非常感谢

【问题讨论】:

  • 我添加了 C++14,因为 make_tuple 在此之前不是 constexpr。

标签: c++ templates c++14 static-members constexpr


【解决方案1】:

看起来您遇到了一个有趣的 decltype 极端情况,clang 错误报告 Static constexpr definitions used inside template 对此进行了介绍,该报告包含以下示例,其中与您的错误类似:

这编译得很好,但是当我创建类 A 时,模板是这样的:

struct L
{
    void operator()() const
    {}
};

template<class X>
struct A
{
    static constexpr auto F = L();
};

template<class X>
constexpr decltype(A<X>::F) A<X>::F;

int main()
{
    A<void>::F();
    return 0;
}

Clang 崩溃,如果我将 F 的定义行更改为:

template<class X>
constexpr typename std::remove_const<decltype(A<X>::F)>::type A<X>::F;

然后clang会产生这个错误:

error: redefinition of 'F' with a different type
constexpr typename std::remove_const<decltype(A<X>::F)>::type A<X>::F;
                                                                    ^
note: previous definition is here
    static constexpr auto F = L();
                          ^

Richard Smith 的回复如下:

这个错误是正确的。 'constexpr' 和 'auto' 在这里是红鲱鱼。 减少测试用例:

template<class X> struct A { static int F; };
template<class X> decltype(A<X>::F) A<X>::F;

根据 C++11 [temp.type]p2,“如果表达式 e 涉及模板 参数,decltype(e) 表示唯一的依赖类型。”因此 A::F 的类型与模板中的类型不匹配。

C++14 草案的完整引用如下:

如果表达式 e 涉及模板参数,则 decltype(e) 表示 一个唯一的依赖类型。两个这样的 decltype 说明符指的是 只有当它们的表达式是等价的 (14.5.6.1) 时才具有相同的类型。 [ 笔记: 但是,它可以是别名,例如,通过 typedef-name。 ——尾注]

我认为完成这项工作的唯一明显方法是:

template<int n>
constexpr decltype(make_tuple(2, "test", 3.4)) Test<n>::invoke;

错误报告中没有提供任何解决方法。

【讨论】:

    【解决方案2】:

    如何让模板版本正常工作?

    FWIW,你的方法在我使用 g++ 4.8.4 的桌面上运行良好。

    你可以使用:

    template<int n>
    constexpr decltype(make_tuple(2, "test", 3.4)) Test<n>::invoke;
    

    以下内容也适用于我的桌面:

    template<int n>
    struct Test {
        static constexpr auto invoke = make_tuple(2, "test", 3.4);
        typedef decltype(invoke) invoke_t;
    };
    
    template<int n>
    constexpr typename Test<n>::invoke_t Test<n>::invoke;
    

    【讨论】:

    • 唯一可行的解​​决方案是decltype(make_tuple(2, "test", 3.4))我使用的是 GCC 5.2.0 和 Clang 3.7.0
    • 奇怪的是 g++ 5.2.0 不工作而 g++ 4.8.4 工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多