Consider the following code:
struct A {
constexpr operator int() { return 42; }
};
template <int>
void foo() {}
void bar(A a) {
foo<a>();
}
int main() {
foo<A{}>();
const int i = 42;
foo<i>(); // (1)
A a{};
static_assert(i == a, "");
bar(a);
foo<a>(); // error here
}
Clang 3.7 with c++14 accepts this, while gcc 5.2.0 with c++14 does not, producing the following message:
/tmp/gcc-explorer-compiler1151027-68-1f801jf/example.cpp: In function 'int main()':
26 : error: the value of 'a' is not usable in a constant expression
foo<a>();
^
23 : note: 'a' was not declared 'constexpr'
A a{};
^
Compilation failed
Changing a
to be constexpr
as suggested by gcc fixes the gcc compilation error, but without constexpr
, which compiler is right?
For me, it seems that a
should be "usable in constant expression", as static_assert
ceritifies. Moreover, the fact that i
can be used the same way (marked (1)
), and the fact that bar()
compiles, also makes me think that gcc is wrong.
UPD: reported a bug against gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68588
The user-defined conversion is allowed by [expr.const]/(4.1), and I don't see a single applicable bullet point in [expr.const]/2 that would prevent your expression from being a constant one. In fact, the requirements are so loose that declaring a
as
A a;
is still giving a well-formed program, even if a
didn't have a constexpr
default constructor etc., since the conversion operator is constexpr
and no members are evaluated.
As you saw yourself, GCC is contradictory in that it allows a
in the static_assert
condition but not a template-argument.
I would say that Clang is correct.
Draft for current C++ (n4296) says:
14.3.2 Template non-type arguments [temp.arg.nontype]
A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of
the type of the template-parameter
And 5.20 §4 says (emphasize mine):
5.20 Constant expressions [expr.const]
...
(4) A converted constant expression of type T is an expression, implicitly converted to type T, where the converted
expression is a constant expression and the implicit conversion sequence contains only
(4.1) — user-defined conversions, ...
IFAIK in foo<a>();
a is converted to int with a constexpr user-defined conversion and as such is a converted constant expression.
That being said, we are not far from a edge case here, and my advice would be: do not play with such a construct in production code :-)
As @Jarod42 suggests a
should be constexpr
. This is because templates are deduced at compile time, and so non-type arguments also have to be available at compile time. constexpr
is a promise that they will be available at compile time.