5

在下文中,static constexpr成员L在类中初始化,A然后通过值或(通用)引用传递。后者在 Clang 中失败,但在 GCC 中失败,并且成员/非成员函数的行为略有不同。更详细地说:

#include <iostream>

using namespace std;

struct A
{
    static constexpr size_t L = 4;

    template <typename T>
    void member_ref(T&& x) { cout << std::forward<T>(x) << endl; }

    template <typename T>
    void member_val(T x) { cout << x << endl; }
};

template <typename T>
void ref(T&& x) { cout << std::forward<T>(x) << endl; }

template <typename T>
void val(T x) { cout << x << endl; }

int main ()
{
    A().member_ref(A::L);  // Clang: linker error: undefined reference to `A::L'
    A().member_val(A::L);  // fine (prints 4)
    ref(A::L);             // Clang: compiles/links fine, no output
    val(A::L);             // fine (prints 4)
}

在将问题从更大的程序中分离出来的一些实验之后,我意识到我不小心使用了constexpr变量的地址,尽管我只对值感兴趣。

我想通过(通用)引用,以便代码是通用的,并且可以在不复制的情况下使用大型结构。我认为您可以通过通用参考传递任何内容,但这里的情况似乎并非如此。我不能使用单独的(类外)定义,L因为这是仅标头库的一部分。

因此,一种解决方法可以是在调用时生成一个值,即说size_t(A::L)或类似的东西A::get_L()而不是 just A::L, where (within class A)

static constexpr size_t get_L() { return L; }

但是这两种解决方案看起来都有些笨拙。在我的实际代码中,调用是在类中进行的,看起来call(0, L, ...)很无辜(0, L看起来像值)。我想让通话尽可能简单。

我认为这个问题及其后续几乎可以解释正在发生的事情。那么任何人都可以建议最干净的方法来处理这个问题吗?

4

1 回答 1

4

您需要A::L在源文件中定义其类之外

constexpr size_t A::L;

使用 Clang 的实时示例

对于仅标头代码,并且如果您的类A还不是模板,则可以A_<T>使用默认值定义类模板,并根据该void值编写 typedefA

template<class = void>
struct A_
{
    static constexpr size_t L = 4;

    template <typename T>
    void member_ref(T&& x) { cout << std::forward<T>(x) << endl; }

    template <typename T>
    void member_val(T x) { cout << x << endl; }

};

template<class T>
constexpr size_t A_<T>::L;

using A = A_<>;

活生生的例子

注意:这项业务可能涉及相当数量的样板。值得注意的是,一个人可以写

template
<
    class MyConcept1, 
    class MyConcept2, 
    class YetAnotherConcept
    // more long and well-documented template parameter names
>
struct A
{
    // many static constexpr variabels
};

template<class P1, class P2, class P3 /* many more short parameter names */>
constexpr SomeType A<P1, P2, P3, /* many more */>::some_var;

// many more one-liners.

模板参数只是有正式的名称,它们不必在任何地方都相同(不过,只需将它们按正确的顺序放在任何地方!)。

于 2014-03-04T13:13:09.587 回答