C++ std::变量转换构造函数不';t handle常量volatile限定符
代码如下:C++ std::变量转换构造函数不';t handle常量volatile限定符,c++,variant,c++-standard-library,C++,Variant,C++ Standard Library,代码如下: int i = 1; const int i_c = 2; volatile int i_v = 3; const volatile int i_cv = 4; typedef std::variant<int, const int, volatile int, const volatile int> TVariant; TVariant var (i ); TVariant var_c (
int i = 1;
const int i_c = 2;
volatile int i_v = 3;
const volatile int i_cv = 4;
typedef std::variant<int, const int, volatile int, const volatile int> TVariant;
TVariant var (i );
TVariant var_c (i_c );
TVariant var_v (i_v );
TVariant var_cv(i_cv);
std::cerr << std::boolalpha;
std::cerr << std::holds_alternative< int>(var ) << std::endl;
std::cerr << std::holds_alternative<const int>(var_c ) << std::endl;
std::cerr << std::holds_alternative< volatile int>(var_v ) << std::endl;
std::cerr << std::holds_alternative<const volatile int>(var_cv) << std::endl;
std::cerr << var .index() << std::endl;
std::cerr << var_c .index() << std::endl;
std::cerr << var_v .index() << std::endl;
std::cerr << var_cv.index() << std::endl;
所以转换构造函数不考虑从类型转换的常量volatile限定符。这是预期行为吗
有关从转换构造函数的信息
构造一个变量,该变量包含替代类型T_j,如果类型中的每个T_i都有虚函数F(T_i)的重载,则该变量将通过重载解析为表达式F(std::forward(T))
选择
问题在于,在上述情况下,此类虚函数的重载集不明确:
void F( int) {}
void F(const int) {}
void F( volatile int) {}
void F(const volatile int) {}
cppreference.com对这起案件只字不提。标准是否规定了这一点
我正在实现自己的
std::variant
类。我的转换构造函数的实现基于。结果与上图相同(选择了第一个合适的备选方案,即使还有其他备选方案)。libstdc++可能以同样的方式实现它,因为它还选择了第一个合适的替代方案。但我仍然怀疑这是否是正确的行为。是的,这就是传递值时函数的工作方式
函数void foo(int)
和函数void foo(const int)
和函数void foo(volatile int)
和函数void foo(const volatile int)
通过扩展,您的变体的转换构造函数没有什么区别,也没有任何有意义的方法来使用变体,因为其替代项仅在顶级cv限定符中有所不同
(好吧,正如Marek所示,你可以用一个显式的模板参数来放置,但是为什么?目的是什么?)
[…]生成参数类型列表后,在形成函数类型时,将删除修改参数类型的任何顶级cv限定符。[……]
请注意,您正在创建值的副本。这意味着可以安全地丢弃
const
和volatile
修饰符。这就是为什么模板总是推断int
您可以使用强制指定类型
参见演示我对标准的理解是,由于模棱两可,代码应该是格式错误的。令我惊讶的是,libstdc++和libc++似乎都允许这样做 下面是[variant.ctor]/12所说的: 让
T
\u j是一种类型,其确定如下:为每个可选类型T
\u i构建一个虚构的函数FUN(T
\u i)。重载解析为表达式FUN(std::forward(T))
选择的重载FUN(T
\u j)定义了可选的T
\u j,它是构造后包含的值的类型
因此创建了四个函数:最初的FUN(int
)、FUN(const-int
)、FUN(volatile-int
)和FUN(const-volatile-int
)。这些都是等效的签名,因此它们不能相互重载。本段并没有具体说明如果无法实际构建重载集,应该怎么做。然而,有一个注释强烈暗示了一种特殊的解释:
[注:变体v(“abc”);
是格式错误的,因为两种可选类型的参数都有一个同样可行的构造函数。-结束注释] 这个注释基本上是说重载解析不能区分
string
和string
。为了实现这一点,必须进行重载解析,即使签名相同。这两个有趣的(string
)并没有折叠成一个函数
<>请注意,过载分辨率允许考虑由于模板导致的具有相同签名的重载。例如:
template <class T> struct Id1 { using type = T; };
template <class T> struct Id2 { using type = T; };
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous
模板结构Id1{using type=T;};
模板结构Id2{using type=T;};
模板void f(typename Id1::type x);
模板void f(typename Id2::type x);
// ...
f(0);//模棱两可的
这里有两个相同的签名f
,它们都提交重载解析,但两者都不比另一个好
回到标准的例子,似乎处方是应用重载解析过程,即使一些重载不能作为普通函数声明彼此重载。(如果您愿意,假设它们都是从模板实例化的。)然后,如果重载解析不明确,则转换构造函数调用的std::variant
格式不正确
注释并没有说
变体示例格式不正确,因为重载解析选择的类型在备选方案列表中出现两次。它说重载解析本身是不明确的(因为这两种类型都有同样可行的构造函数)。这一区别很重要。如果此示例在重载解析阶段后被拒绝,则可能会有一个参数表明您的代码格式正确,因为顶级cv限定符将从参数类型中删除,从而使所有四个重载都变得有趣(int
),从而T
\u j=int
)。但由于注释表明过载解决过程中出现故障,这意味着您的示例不明确(因为4个签名是等效的),必须对此进行诊断。注意auto a=var_c
将丢弃const
,而auto b=var_v
将丢弃volatile
,这是您问题的根源<调用构造函数时,code>int
总是自动推断。@MarekR自动推断在哪里?Variant的构造函数使用转发引用,其中const
和volatile
限定符保存在模板参数IIRC()中。我相信问题出在那些忽略价值的人身上。这些警告有点令人担忧——它们是什么意思?@LightnessRacesinOrbit-See<代码>放置
返回co
template <class T> struct Id1 { using type = T; };
template <class T> struct Id2 { using type = T; };
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous