未定义的引用,模板结构体和constexpr静态成员

7

我遇到了一个与模板结构体中的静态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。 - Shafik Yaghmour
2个回答

4

看起来你正在使用decltype时遇到一个有趣的边角案例,这在clang的错误报告“模板中使用静态constexpr定义”中有所涉及,其中包含了一个类似于你的错误的示例:

This compiles fine, but when I make the class A, a template like this:

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 crashes, if I change the definition line for F to this:

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

Then clang produces this error:

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的回答如下:

This error is correct. 'constexpr' and 'auto' are red herrings here. Reduced testcase:

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

Per C++11 [temp.type]p2, "If an expression e involves a template parameter, decltype(e) denotes a unique dependent type." Therefore the type of A::F does not match the type in the template.

从C++14草案中可以得到完整的引用如下:
如果表达式e涉及模板参数,则decltype(e)表示唯一的依赖类型。仅当它们的表达式等效时,两个这样的decltype-specifier才引用同一类型(14.5.6.1)。[注意:但是,它可能被typedef名称别名。-注]。
我能想到唯一明显的使其工作的方法是:
template<int n>
constexpr decltype(make_tuple(2, "test", 3.4)) Test<n>::invoke;

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

3

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

就我个人而言,您的方法在我的桌面电脑上运行良好,该电脑使用的是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;

1
似乎唯一可以工作的解决方案是 decltype(make_tuple(2, "test", 3.4))。我正在使用 GCC 5.2.0 和 Clang 3.7.0。 - Guillaume Racicot
很奇怪,g++ 5.2.0 不起作用,而 g++ 4.8.4 却可以。 - R Sahu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接