不同编译器上 `std::tuple_size_v` 的 SFINAE 行为不同

11

考虑下面这段代码:

#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 << '\n';
}

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<...>::value替换std::tuple_size_v<...>来修复它,然后在所有三个编译器上都打印0

1
我认为如果你在我的删除回答下融入我们讨论的一些内容,这个问题将会更有趣。使用 size_t 或不使用 size_t 可以更明确地表述。 - StoryTeller - Unslander Monica
看起来GCC对函数模板和变量模板的处理方式不同。我敢打赌这是GCC(和MSVC)的一个bug。 - xskxzr
CWG2222? - Language Lawyer
@LanguageLawyer 嗯,虽然它没有提到变量模板。 - HolyBlackCat
1
@HolyBlackCat 注意,我将默认模板参数更改为std::size_t - xskxzr
显示剩余4条评论
1个回答

7
我认为Clang是正确的。 [temp.inst]/7中的规则是:
除非变量模板特化是已声明的特化,否则当在需要存在变量定义的上下文中引用变量模板特化或者定义的存在影响程序语义时,将隐式实例化变量模板特化。
这个程序不需要std::tuple_size_v<std::string>定义,只需要声明。而声明如下:
template <typename T>
inline constexpr size_t tuple_size_v = tuple_size<T>::value;

足以在部分特化中评估表达式。对于任何size_tdecltype(std::tuple_size_v<T>, void())根本不依赖于此处的值,这是一个有效的void类型表达式。
如果我们处理的是函数模板而不是变量模板:
template <typename T>
constexpr size_t tuple_size_v() { return tuple_size<T>::value; }

很明显我们不需要定义,只需要声明,gcc和msvc都接受这种替代形式(实际上,甚至会警告它是无意义的):example

稍后,在[temp.inst]/8中,我们有:

如果变量或函数在表达式([expr.const])的常量求值中被需要,即使不需要对表达式进行常量求值或常量表达式求值不使用定义,变量或函数的定义的存在也被认为影响程序的语义。

但在这里并非如此:我们不需要该变量进行常量求值。


有趣。在xskxzr在评论中提供的示例中,Clang无法做到同样的事情。 - HolyBlackCat
@HolyBlackCat 是的,这是同样的事情 - 你只需要 decltype(tuple_size_v<T>),你就知道它是 size_t 而不需要知道它的值。 - Barry
有一件事我不确定:标准是否甚至有一个概念,即将变量模板声明与其定义分开实例化?我可以找到关于“实例化函数声明”的内容,但没有关于变量声明的内容。 - HolyBlackCat

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