I expect the following code to fail with a static_assert
check on the final line. However in MSVC2015 and gcc 6.2, it compiles sucessfully. It does fail to compile as expected in clang 3.9. Is this a compiler bug or does static_assert
not work inside 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
Update #1: Additional Question
Brian suggested that the static_assert
would not fire in the decltype
context because the value has not been explicitly instantiated. So I added an additional test below to explicitly instantiate the shouldFail2
type , which I think by Brian's logic should cause the However, the code below does not fail in MSVC2015 or gcc 6.2. static_assert
to fail.Is this one a bug, or have I overlooked something? Edit: It appears that once decltype
has extracted the type, we are free to use shouldFail2
without further reference to the definition of operator|
.
shouldFail2 shouldFail3 = {}; // instantiate shouldFail2.
// ^ doesn't fail in MSVC2015 or gcc 6.2.
Update #2
If I change the definition of operator|
to use an auto
(or decltype(auto)
) with no trailing return type, then the decltype
expression correctly fails the static_assert
in gcc 6.2. However this version fails to compile in MSVC2015 (errors C3779, C2088). Edit: as W.F. points out below, omitting the trailing return type is a C++14 feature.
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
I believe GCC and MSVC are correct, and Clang is incorrect. The static_assert
should not fire, because according to the Standard at [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.
Inside an unevaluated context such as decltype
, it is valid to have a call to a function that is left undefined, so this is not such a context in which the function definition is required to exist. Therefore the static_assert
declaration in the body of the specialization is not instantiated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With