通过通用引用传递一个静态constexpr变量? [英] passing a static constexpr variable by universal reference?

查看:177
本文介绍了通过通用引用传递一个静态constexpr变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面, static constexpr member 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 使用单独的(out-of-class)定义,因为这是一个只有头文件的库的一部分。



所以一个解决方法是在调用时生成一个值,例如 size_t(A :: L)或类似 A :: get_L()而不是 A :: L ,其中(在 A

  static constexpr size_t get_L(){return L; } 

但两个解决方案看起来有点笨拙。在我的实际代码中,调用是在类中,看起来像调用(0,L,...)这看起来很无辜( L 看上去像值)。我想尽可能简化通话。



我认为此问题它的后续几乎解释了发生了什么。

解决方案

你需要定义 A :: L 在源文件中类的外部

  constexpr size_t A :: L; 

活动示例 使用Clang



对于纯头文件代码,如果您的类 A 不是模板,您可以使用 void 定义类模板 A_< T& code>默认值,并为 A 编写typedef

  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;

使用A = A_<> ;;

: 锅炉板数量。值得注意的是,可以写

 模板
<
类MyConcept1,
类MyConcept2,
类YetAnotherConcept
//更长的和详细的模板参数名称
>
struct A
{
//许多静态constexpr variabels
};

模板<类P1,类P2,类P3 / *更多的短参数名称* />
constexpr SomeType A< P1,P2,P3,/ * many more * /> :: some_var;

//更多的单行。

模板参数只是有正式名称,它们不必在任何地方都一样在正确的顺序无处不在,尽管!)。


In the following, static constexpr member L is initialized in-class A and then passed by value or by (universal) reference. The latter fails in Clang but not in GCC, and behaviour is slightly different for member/non-member functions. In more detail:

#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)
}

After some experimentation in isolating the problem from a larger program, I realized that I am accidentally using the address of a constexpr variable, although I am only interested in the value.

I want to pass by (universal) reference so that the code is generic and works with large structures without copying. I thought that you could pass anything with a universal reference but it appears this is not the case here. I cannot use a separate (out-of-class) definition for L because this is part of a header-only library.

So one workaround can be to generate a value upon call, i.e. say size_t(A::L) or something like A::get_L() instead of just A::L, where (within class A)

static constexpr size_t get_L() { return L; }

but both solutions look a bit clumsy. In my actual code the call is made within the class and looks like call(0, L, ...) which appears quite innocent (0, L look like values). I'd like to keep the call as simple as possible.

I think this question and its follow-up pretty much explain what is happening. So could anyone suggest what would be the cleanest way to deal with this?

解决方案

You need to define A::L outside its class in a source file

constexpr size_t A::L;

Live example using Clang

For header-only code, and if your class A is not already a template, you can define a class template A_<T> with a void default value, and write a typedef for A in terms of that

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

Live Example.

NOTE: this business can involve a fair amount of boiler-plate. It is good to note that one can write

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.

Template parameters just have formal names, they don't have to be the same everywhere (just put them in the right order everywhere, though!).

这篇关于通过通用引用传递一个静态constexpr变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆