当通过decltype表达式调用时,static_assert是否可以工作?

Ros*_*ina 9 c++ static-assert decltype c++11

我希望以下代码失败并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
Run Code Online (Sandbox Code Playgroud)

更新#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.
Run Code Online (Sandbox Code Playgroud)

更新#2

如果我更改了operator|使用没有尾随返回类型的auto(或decltype(auto))的定义,那么decltype表达式正确地使static_assertin gcc 6.2 失败.但是,此版本无法在MSVC2015中编译(错误C3779,C2088).编辑:正如WF在下面指出的那样,省略尾随返回类型是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
Run Code Online (Sandbox Code Playgroud)

Bri*_*ian 7

我相信GCC和MSVC是正确的,但Clang是不正确的.所述static_assert不应该火,因为根据标准在[temp.inst]/3:

除非已明确实例化或明确专门化了函数模板特化,否则在需要存在函数定义的上下文中引用特化时,将隐式实例化函数模板特化.

在未评估的上下文中decltype,如果对未定义的函数进行调用是有效的,因此这不是需要存在函数定义的上下文.因此,static_assert专业化主体中的声明不会被实例化.

  • @RossBencina如果你使返回类型为"auto",那么定义必须存在,以便可以推导出返回类型.所以在这种情况下,`static_assert`肯定会激发. (2认同)