Different SFINAE behavior of `std::tuple_size_v` on different compilers(`std::tuple_size_v`在不同编译器上的不同SFINAE行为)
问题描述
考虑以下代码:
#include <tuple>
#include <type_traits>
#include <iostream>
template <typename T, typename = void> struct is_tuple_like : std::false_type {};
template <typename T> struct is_tuple_like<T, decltype(std::tuple_size_v<T>, void())> : std::true_type {};
int main()
{
std::cout << is_tuple_like<std::string>::value << '
';
}
Run on gcc.godbolt.org
在GCC 10.2和MSVC19.28上,它会导致硬错误,大致如下:
error: incomplete type 'std::tuple_size<...>' used in nested name specifier
另一方面,在Clang 11.0.1上,它使用libstdc++和libc++编译和打印1
。
此处哪个编译器正确?
请注意,Clang打印1
而不是0
,这意味着它不会将std::tuple_size<std::string>::value
(tuple_size_v
的初始值设定项)视为软错误,而是选择完全忽略它!
这在某种程度上是有道理的,因为如果tuple_size_v
定义为template <typename T> inline constexpr size_t tuple_size_v = ...
,则decltype(tuple_size_v<...>)
类型不依赖于模板参数,始终为size_t
。
我想问题可以归结为是否需要在此处实例化tuple_size_v
的初始值设定项,即使它不是严格必需的。
我知道我可以通过将std::tuple_size_v<...>
替换为std::tuple_size<...>::value
来修复它,然后它会在所有三个编译器上打印0
。
推荐答案
我认为Lang有这个权利。
[temp.inst]/7中的规则为:
除非变量模板专门化是声明的专门化,否则在需要存在变量定义的上下文中引用变量模板专门化时,或者如果该定义的存在影响程序的语义,则会隐式实例化该变量模板专门化。
此程序不需要std::tuple_size_v<std::string>
的定义,只需要声明。和声明:
template <typename T>
inline constexpr size_t tuple_size_v = tuple_size<T>::value;
足以计算部分专门化中的表达式。decltype(std::tuple_size_v<T>, void())
根本不依赖于此处的值,对于任何size_t
,这都是void
类型的有效表达式。
我们处理的是函数模板而不是变量模板:
template <typename T>
constexpr size_t tuple_size_v() { return tuple_size<T>::value; }
可能更清楚的是,我们不需要定义,只需要声明,GCC和MSVC都接受这种替代提法(事实上,GCC甚至警告它没有意义):example。
稍后,在[temp.inst]/8中,我们有:
如果某个表达式([expr.const])的常量求值需要该变量或函数,即使不需要该表达式的常量求值,或者常量表达式求值不使用该定义,则认为该变量或函数的定义的存在会影响程序的语义。
但情况并非如此:我们不需要变量来进行常量计算。
这篇关于`std::tuple_size_v`在不同编译器上的不同SFINAE行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:`std::tuple_size_v`在不同编译器上的不同SFINAE行为
基础教程推荐
- 如何定义双括号/双迭代器运算符,类似于向量的向量? 2022-01-01
- 如何在 C++ 中处理或避免堆栈溢出 2022-01-01
- C++,'if' 表达式中的变量声明 2021-01-01
- 设计字符串本地化的最佳方法 2022-01-01
- 您如何将 CreateThread 用于属于类成员的函数? 2021-01-01
- C++ 标准:取消引用 NULL 指针以获取引用? 2021-01-01
- C++ 程序在执行 std::string 分配时总是崩溃 2022-01-01
- 运算符重载的基本规则和习语是什么? 2022-10-31
- 什么是T&&(双与号)在 C++11 中是什么意思? 2022-11-04
- 调用std::Package_TASK::Get_Future()时可能出现争用情况 2022-12-17