static_assert 应该在通过 decltype 表达式调用时起作用吗?
Is static_assert supposed to work when invoked via decltype expression?
我预计以下代码会失败,并在最后一行进行 static_assert
检查。但是在 MSVC2015 和 gcc 6.2 中,它编译成功。它确实无法按预期在 clang 3.9 中进行编译。这是编译器错误还是 static_assert
在 decltype()
中不起作用?
#include <tuple>
#include <type_traits>
template<typename T>
struct Wrapper {};
template<typename T, typename U>
constexpr std::tuple<T, U> operator|(Wrapper<T>, Wrapper<U>)
{
static_assert(std::is_same<T,U>::value == false, "can't combine two of the same type");
return std::tuple<T, U> {};
}
struct A {};
struct B {};
constexpr Wrapper<A> aaa = {};
constexpr Wrapper<B> bbb = {};
constexpr auto shouldPass1 = aaa | bbb;
//constexpr auto shouldFail1 = aaa | aaa; // fails static assert as expected
using shouldFail2 = decltype(aaa | aaa);
// ^ doesn't fail in MSVC2015, or gcc 6.2. does fail in clang 3.9
更新 #1:附加问题
Brian 建议 static_assert
不会在 decltype
上下文中触发,因为该值尚未明确实例化。所以我在下面添加了一个额外的测试来显式实例化 shouldFail2
类型 ,我认为按照 Brian 的逻辑应该会导致 static_assert
失败。 然而,下面的代码在 MSVC2015 或 gcc 6.2 中不会失败。 这是一个错误,还是我忽略了什么? 编辑:似乎一旦 decltype
提取了类型,我们就可以自由使用 shouldFail2
而无需进一步参考operator|
.
的定义
shouldFail2 shouldFail3 = {}; // instantiate shouldFail2.
// ^ doesn't fail in MSVC2015 or gcc 6.2.
更新#2
如果我将 operator|
的定义更改为使用没有尾随 return 类型的 auto
(或 decltype(auto)
),则 decltype
表达式gcc 6.2 中的 static_assert
正确失败。但是,此版本无法在 MSVC2015 中编译(错误 C3779、C2088)。编辑:如 W.F。下面指出,省略尾随 return 类型是 C++14 的特性。
template<typename T, typename U>
constexpr auto operator|(Wrapper<T>, Wrapper<U>)
{
static_assert(std::is_same<T,U>::value == false, "can't combine two of the same type");
return std::tuple<T, U> {};
}
...
using shouldFail2 = decltype(aaa | aaa);
// ^ now this correctly fails the static_assert in gcc 6.2
我认为 GCC 和 MSVC 是正确的,Clang 是错误的。 static_assert
不应触发,因为根据 [temp.inst]/3:
的标准
Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that
requires a function definition to exist.
在未计算的上下文中,例如 decltype
,调用未定义的函数是有效的,因此这不是要求函数定义存在的上下文。因此,专业化主体中的 static_assert
声明未实例化。
我预计以下代码会失败,并在最后一行进行 static_assert
检查。但是在 MSVC2015 和 gcc 6.2 中,它编译成功。它确实无法按预期在 clang 3.9 中进行编译。这是编译器错误还是 static_assert
在 decltype()
中不起作用?
#include <tuple>
#include <type_traits>
template<typename T>
struct Wrapper {};
template<typename T, typename U>
constexpr std::tuple<T, U> operator|(Wrapper<T>, Wrapper<U>)
{
static_assert(std::is_same<T,U>::value == false, "can't combine two of the same type");
return std::tuple<T, U> {};
}
struct A {};
struct B {};
constexpr Wrapper<A> aaa = {};
constexpr Wrapper<B> bbb = {};
constexpr auto shouldPass1 = aaa | bbb;
//constexpr auto shouldFail1 = aaa | aaa; // fails static assert as expected
using shouldFail2 = decltype(aaa | aaa);
// ^ doesn't fail in MSVC2015, or gcc 6.2. does fail in clang 3.9
更新 #1:附加问题
Brian 建议 static_assert
不会在 decltype
上下文中触发,因为该值尚未明确实例化。所以我在下面添加了一个额外的测试来显式实例化 shouldFail2
类型 ,我认为按照 Brian 的逻辑应该会导致 然而,下面的代码在 MSVC2015 或 gcc 6.2 中不会失败。 static_assert
失败。这是一个错误,还是我忽略了什么? 编辑:似乎一旦 decltype
提取了类型,我们就可以自由使用 shouldFail2
而无需进一步参考operator|
.
shouldFail2 shouldFail3 = {}; // instantiate shouldFail2.
// ^ doesn't fail in MSVC2015 or gcc 6.2.
更新#2
如果我将 operator|
的定义更改为使用没有尾随 return 类型的 auto
(或 decltype(auto)
),则 decltype
表达式gcc 6.2 中的 static_assert
正确失败。但是,此版本无法在 MSVC2015 中编译(错误 C3779、C2088)。编辑:如 W.F。下面指出,省略尾随 return 类型是 C++14 的特性。
template<typename T, typename U>
constexpr auto operator|(Wrapper<T>, Wrapper<U>)
{
static_assert(std::is_same<T,U>::value == false, "can't combine two of the same type");
return std::tuple<T, U> {};
}
...
using shouldFail2 = decltype(aaa | aaa);
// ^ now this correctly fails the static_assert in gcc 6.2
我认为 GCC 和 MSVC 是正确的,Clang 是错误的。 static_assert
不应触发,因为根据 [temp.inst]/3:
Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist.
在未计算的上下文中,例如 decltype
,调用未定义的函数是有效的,因此这不是要求函数定义存在的上下文。因此,专业化主体中的 static_assert
声明未实例化。