Calling constexpr in default template argument - c++

In C++11 I am using a constexpr function as a default value for a template parameter - it looks like this:
template <int value>
struct bar
{
static constexpr int get()
{
return value;
}
};
template <typename A, int value = A::get()>
struct foo
{
};
int main()
{
typedef foo<bar<0>> type;
return 0;
}
G++ 4.5 and 4.7 compiles this, but Clang++ 3.1 does not. The error message from clang is:
clang_test.cpp:10:35: error: non-type template argument is not a constant expression
template <typename A, int value = A::get()>
^~~~~~~~
clang_test.cpp:17:19: note: while checking a default template argument used here
typedef foo<bar<3>> type;
~~~~~~~~~^~
clang_test.cpp:10:35: note: undefined function 'get' cannot be used in a constant expression
template <typename A, int value = A::get()>
^
clang_test.cpp:4:23: note: declared here
static constexpr int get()
^
1 error generated.
Which one is correct?

Richard Smith (zygoloid) at the LLVM IRC channel had a short talk with me about this issue which is your answer
<litb> hello folks
<litb> zygoloid, what should happen in this case?
<litb> http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument
<litb> it seems to be clang's behavior is surprising
<litb> zygoloid, i cannot apply the "point of instantiation" rule to constexpr
function templates. if i call such a function template, the called definition's
POI often is *after* the specialization reference, which means at the point of
the call, the constexpr function template specialization is "undefined".
<zygoloid> it's a horrible mess. Clang does not do what the standard intends, but
as you note, the actual spec is gloriously unclear
<d0k> :(
<zygoloid> we should instantiate bar<3>::get(), because it is odr-used, but we
don't, because we incorrectly believe it's used in an unevaluated context
<zygoloid> conversely, the point of instantiation is too late :/
<zygoloid> PR11851
So it seems that sometimes, Clang instantiates called function templates or member function of class templates but their instantiation is too late for the call to see, and at other cases it doesn't even instantiate them because it thinks it will never need them (unevaluated context).

I think GCC Clang is correct
quoted from n3290:
14.3.2 Template non-type arguments [temp.arg.nontype]
A template-argument for a non-type, non-template template-parameter shall be one of:
for a non-type template-parameter of integral or enumeration type, a converted > constant expression (5.19) of the type of the template-parameter; or
...
EDIT: 5.19 3
A literal constant expression is a prvalue core constant expression of
literal type, but not pointer type. An integral constant expression is
a literal constant expression of integral or unscoped enumeration
type. [ Note: Such expressions may be used as array bounds (8.3.4,
5.3.4), as bit-field lengths (9.6), as enumerator initializers if the underlying type is not fixed (7.2), as null pointer constants (4.10),
and as alignments (7.6.2). —end note ] A converted constant expression
of type T is a literal constant expression, implicitly converted to
type T, where the implicit conversion (if any) is permitted in a
literal constant expression and the implicit conversion sequence
contains only user-defined conversions, lvalue-to-rvalue conversions
(4.1), integral promotions (4.5), and integral conversions (4.7) other
than narrowing conversions (8.5.4).
[ Note: such expressions may be used as case expressions (6.4.2), as enumerator initializers if the underlying type is fixed (7.2), and as integral or enumeration non-type template arguments (14.3). —end note ]

Related

Expected behavior on out-of-range template parameters?

template<bool b = 2> void foo(void) {}
template void foo();
template<unsigned char n = 258> void bar(void) {}
template void bar();
GCC instantiates foo< true> and bar<2>; Clang rejects both with "error: non-type template argument evaluates to 2, which cannot be narrowed to type 'bool' [-Wc++11-narrowing]".
Is the above code valid? Is this a bug in one of them?
Versions used: Clang 3.8.0-2ubuntu4, GCC 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.2)
This is gcc bug 57891 and 60715.
From [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.
From [expr.const]:
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 [...] integral conversions (4.7) other than narrowing conversions (8.5.4),
From [dcl.init.list]:
A narrowing conversion is an implicit conversion [...] from an integer type or unscoped enumeration type to an integer type that cannot represent all the
values of the original type, except where the source is a constant expression whose value after integral
promotions will fit into the target type.
Narrowing conversions (e.g. 2 to bool or 258 to char) are ill-formed for template non-type parameters.

How implicit conversion works for non-type template parameters?

I guess (certain) implicit conversions apply when passing non-type template parameters. For example, there should be a conversion from int to std::size_t for expressions like std::array<int, 7>. However, consider the following code:
template <bool>
void f() {
std::cout << "false\n";
}
template <>
void f<true>() {
std::cout << "true\n";
}
int main() {
f<1>();
f<4>();
f<0>();
}
I expect int to be implicitly converted to bool here. But the behaviors are different on VC, GCC, and clang.
On VC, true, false, and false are printed, which is really weird to me.
On GCC, true, true, and false are printed, which is what I expect.
While on clang, the code does not compile at all due to the statement f<4>();.
candidate template ignored: invalid explicitly-specified argument for 1st template parameter
So, what does the standard say about this? What is the implicit conversion rule for non-type template parameters?
From the standard (§14.3.2/5):
The following conversions are performed on each expression used as a non-type template-argument. If a
non-type template-argument cannot be converted to the type of the corresponding template-parameter then
the program is ill-formed.
For a non-type template-parameter of integral or enumeration type, conversions permitted in a converted constant expression (5.19) are applied.
In §5.19, we learn (emphasis mine):
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted
to a prvalue, where the converted expression is a core constant expression. ... A converted constant expression of
type T is an expression, implicitly converted to a prvalue of type T, where the converted expression is a core
constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue
conversions (4.1), integral promotions (4.5), and integral conversions (4.7) other than narrowing conversions
(8.5.4). [ Note: such expressions may be used in new expressions (5.3.4), as case expressions (6.4.2),
as enumerator initializers if the underlying type is fixed (7.2), as array bounds (8.3.4), and as integral or
enumeration non-type template arguments (14.3). —end note ]
So narrowing conversions (like converting 4 to bool) are explicitly disallowed for integral constant expressions, which are required in this case as a non-type template argument. That makes the call f<4>() ill-formed.
I believe Clang is correct in issuing an error, and GCC and VC are both nonconforming for not issuing any diagnostic.
[Update] This is GCC Bug #57891, looks like it's currently unassigned.

pointers to object with external linkage to Nontype template parameters

I tried the following code.
template <int VAL>
void printVAL()
{
for(int i=0;i<VAL; i++){
cout << " i value is "<<i<<endl;
}
}
instantiation:
printVAL<100>()
When i use (std::string s ) as a non type template parameter, the compiler shouted at me with the following error
"class std::basic_str<char>' is not a valid type for a template non-type parameter.
What i know is we should use only constant integral values only. Not even double.
Question:
1) why we should not use std::string, what bothers ?
2) What is the meaning of 'pointers to objects with external linkage can be used'. can i get any sample code for it?
According to 14.1/4,
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
integral or enumeration type,
pointer to object or pointer to function,
lvalue reference to object or lvalue reference to function,
pointer to member,
std::nullptr_t.
Your std::string is none of those.
You can only pass integral values as non-type template parameters. That's a limitation of the language. You probably want something as follows:
template<class T>
void printVAL(const T& t)
{
for (auto& x : t) {
cout << " x value is "<< x << '\n';
}
}
This would print anything: containers, strings, arrays, etc.
There are two types of templates: Templates for generic types, and templates with specified types.
The templates for generic types like like
template<typename T>
or
template<class T>
These templates can be used with just about any type. The actual usage of these types may limit the actual types that can be use though.
Templates with specified types are like the one you have in your question:
template<int VAL>
They can only be used with template arguments matching the specified type. In this case only integer literals can be used.
1) why we should not use std::string, what bothers ?
Compiler must be able to deduce and substitute template parameters at compile time. This can't be done with a std::string object, it could (and usualy does) change at runtime.
See also this Q&A.
2) What is the meaning of 'pointers to objects with external linkage can be used'. can i get any sample code for it?
That's because the address of an object with static storage duration (which object declared extern are) is a constant expression.
n3337 5.19/3, emphasis mine:
A literal constant expression is a prvalue core constant expression of literal type, but not pointer type. An
integral constant expression is a literal constant expression of integral or unscoped enumeration type. [ Note:
Such expressions may be used as array bounds (8.3.4, 5.3.4), as bit-field lengths (9.6), as enumerator initializ-
ers if the underlying type is not fixed (7.2), as null pointer constants (4.10), and as alignments (7.6.2). —end
note ] A converted constant expression of type T is a literal constant expression, implicitly converted to type T,
where the implicit conversion (if any) is permitted in a literal constant expression and the implicit conversion
sequence contains only user-defined conversions, lvalue-to-rvalue conversions (4.1), integral promotions (4.5),
and integral conversions (4.7) other than narrowing conversions (8.5.4). [ Note: such expressions may be used
as case expressions (6.4.2), as enumerator initializers if the underlying type is fixed (7.2), and as integral or
enumeration non-type template arguments (14.3). —end note ] A reference constant expression is an lvalue
core constant expression that designates an object with static storage duration or a function. An address
constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an
object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core
constant expression of type std::nullptr_t. Collectively, literal constant expressions, reference constant
expressions, and address constant expressions are called constant expressions.
Example you ask for:
#include <iostream>
#include <string>
extern std::string str; // not really neccesary
std::string str;
template<std::string* ptr>
struct S {
S() { std::cout << ptr->length() << '\n'; }
~S() { std::cout << *ptr; }
};
int main()
{
S<&str> s;
str = "Hello world!";
}

Why can't I downcast pointer to members in template arguments?

If I make a pointer-to-base-member, I can convert it to a pointer-to-derived-member usually, but not when used within a template like Buzz below, where the first template argument influences the second one. Am I fighting compiler bugs or does the standard really mandate this not work?
struct Foo
{
int x;
};
struct Bar : public Foo
{
};
template<class T, int T::* z>
struct Buzz
{
};
static int Bar::* const workaround = &Foo::x;
int main()
{
// This works. Downcasting of pointer to members in general is fine.
int Bar::* y = &Foo::x;
// But this doesn't, at least in G++ 4.2 or Sun C++ 5.9. Why not?
// Error: could not convert template argument '&Foo::x' to 'int Bar::*'
Buzz<Bar, &Foo::x> test;
// Sun C++ 5.9 accepts this but G++ doesn't because '&' can't appear in
// a constant expression
Buzz<Bar, static_cast<int Bar::*>(&Foo::x)> test;
// Sun C++ 5.9 accepts this as well, but G++ complains "workaround cannot
// appear in a constant expression"
Buzz<Bar, workaround> test;
return 0;
}
It simply isn't allowed. According to §14.3.2/5:
The following conversions are performed on each expression used as a non-type template-argument. If a non-type template-argument cannot be converted to the type of the corresponding template-parameter then the program is ill-formed.
— for a non-type template-parameter of integral or enumeration type, integral promotions (4.5) and integral conversions (4.7) are applied.
— for a non-type template-parameter of type pointer to object, qualification conversions (4.4) and the array-to-pointer conversion (4.2) are applied.
— For a non-type template-parameter of type reference to object, no conversions apply. The type referred to by the reference may be more cv-qualified than the (otherwise identical) type of the template argument. The template-parameter is bound directly to the template-argument, which must be an lvalue.
— For a non-type template-parameter of type pointer to function, only the function-to-pointer conversion (4.3) is applied. If the template-argument represents a set of overloaded functions (or a pointer to such), the matching function is selected from the set (13.4).
— For a non-type template-parameter of type reference to function, no conversions apply. If the template-argument represents a set of overloaded functions, the matching function is selected from the set (13.4).
— For a non-type template-parameter of type pointer to member function, no conversions apply. If the template-argument represents a set of overloaded member functions, the matching member function is selected from the set (13.4).
— For a non-type template-parameter of type pointer to data member, qualification conversions (4.4) are applied.
I've emphasized the conversion regarding pointer to data members. Note that your conversion (§4.11/2) is not listed. In C++0x, it remains the same in this regard.

0 not a valid FILE* when provided as a template argument

The following code
#include <stdio.h>
template <typename T, T v> class Tem
{
T t;
Tem()
{
t = v;
}
};
typedef Tem<FILE*,NULL> TemFile;
when compiled in a .mm file (Objective C++) by Xcode on MacOS X, throws the following error:
error: could not convert template argument '0' to 'FILE*'.
What's going on, please? The code in question compiled fine under MSVC. Since when is the 0 constant not a valid pointer to anything? Is this an artifact of Objective C++ (as opposed to vanilla C++)?
According to the standard, you are out of luck. There is no way to initialize a pointer argument to anything besides the address-of a global. §14.3.2/1:
A template-argument for a non-type,
non-template template-parameter shall
be one of:
an integral constant-expression of integral or enumeration type; or
the name of a non-type template-parameter; or
the address of an object or function with external linkage, including
function templates and function
template-ids but excluding non-static
class members, expressed as &
id-expression where the & is optional
if the name refers to a function or
array, or if the corresponding
template-parameter is a reference; or
a pointer to member expressed as described in 5.3.1 .
§14.3.2/5:
for a non-type template-parameter of
type pointer to object, qualification
conversions (4.4) and the
array-to-pointer conversion (4.2) are
applied. [Note: In particular, neither
the null pointer conversion (4.10) nor
the derived-to-base conversion (4.10)
are applied. Although 0 is a valid
template-argument for a non-type
template-parameter of integral type,
it is not a valid template-argument
for a non-type template-parameter of
pointer type. ]
However, Comeau accepts this invalid workaround:
typedef Tem<FILE*, (FILE *) NULL > TemFile;
And this code has a slim chance of compliance: I can't find where the standard specifically says that a default expression is used verbatim in place of a a missing argument, and I can't find a matching known defect. Anyone have a reference?
#include <stdio.h>
template <typename T, T *v = (T*) 0> class Tem
{
T t;
Tem()
{
t = v;
}
};
typedef Tem<FILE> TemFile;
For more portability, you might consider creating a bogus FILE FILE_NULL;, pass &FILE_NULL, and test for pointer-equality with that instead of zero.
Did you try something like this?
typedef Tem<FILE*,((FILE*)NULL)> TemFile;
Perhaps it's trying to figure out the type of NULL.