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.
Related
Consider the following class S containing a function pointer, and a constexpr object s of that class initialized with a lambda:
struct S
{
void (*f) ();
};
constexpr S s { []{} };
Now if I write a template X with a non-type template parameter of type S, and instantiate it over s like this:
template<S> struct X {};
using x = X<s>;
clang compiles the code, but gcc complains with:
error: '<lambda()>::_FUN' is not a valid template argument of type 'void (*)()' because '<lambda()>::_FUN' is not a variable
using x = X<s>;
^
Here's the program.
The code seems fine to me, and I'm not sure what the error message is referring to. So is this code valid?
Note that both compilers accept the code if X instead has a non-type template parameter of reference type to S, like this:
template<S const &> struct X {};
This question was inspired by another similar question.
The code is valid.
[temp.arg.nontype]/2:
A template-argument for a non-type template-parameter shall be a converted constant expression ([expr.const]) of the type of the template-parameter.
[expr.const]/10:
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 [...]
(There's no implicit conversion in this case.)
[expr.const]/11:
A constant expression is [...] or a prvalue core constant expression whose value satisfies the following constraints:
if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
if the value is of pointer type, it contains [...] the address of a non-immediate function [...],
if the value is of pointer-to-member-function type, [...],
if the value is an object of class or array type, each subobject satisfies these constraints for the value.
An entity is a permitted result of a constant expression if [...] or if it is a non-immediate function.
(All functions are non-immediate except for the consteval ones. [dcl.constexpr])
Therefore, s is a valid converted constant expression of type S. In addition, it is not subject to [temp.arg.nontype]/3 (which only applies to pointer/reference to objects).
That is, s is a valid template argument.
I'm not sure what the error message is referring to
It's nonsense.
The error is emitted from invalid_tparm_referent_p inside GCC. It was extracted from code for handling pointer to object type when class non-type template paramter was implemented (4be5c72). Apparently, the implementer forgot to update this function to account for the pointer-to-function case.
I've reported the bug as https://gcc.gnu.org/PR97700.
I've distilled a problem that's confounded me to the minimal example shown below. Basically the gcc compiler doesn't accept NULL as a template parameter of type void*.
Is there a workaround for this?
Please note that I'm restricted to C++03.
#define NULL 0
template<typename T = void , T* = NULL>
struct Foo
{
};
typedef Foo<> FooType;
int main()
{
}
Edit: online version
There is no solution to your problem: C++03 pointer template parameters have to designate an object. Using NULL as a non-type template parameter is a feature that was added in N1905, which came out at least one year after C++03.
14.3.2 Template non-type arguments
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
(NEW) a constant expression that evaluates to a null pointer value (4.10); or
(NEW) a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1.
One option is to declare a "null struct" with a member that you can use instead of a real NULL:
template<typename T>
struct null
{
static T value;
};
template<typename T, T* = &null<T>::value>
struct Foo
{
};
Of course, accessing null<T>::value has well-defined semantics and it won't crash; the purpose is just to have an address that is guaranteed to be different from the address of any other object.
Note that in any case, it will be impossible for you to use T = void. You can't use the null workaround with it because void is an incomplete type; and you can't cast anything to a void pointer to use as a non-type template argument.
I have template class C that has a non-type but reference template parameter to a type P:
class P {
public:
int x;
int y;
};
template <const P &x>
class C {
public:
const int &f() { return x.x; }
};
I declared a global variable of type P:
P p = {33,44};
I also declared a function that returns a reference to p:
constexpr const P &h() { return p; }
And then tried to use these in the following :
C<p> o; // line 1
C<h()> oo; // line 2
Of course I have no problem with the first instantiation but the second. My compiler complains:
error: non-type template argument does not refer to any declaration
Why is so ? I was unable to find an argument against it in the norm. I am not sure that it is exactly the same problem as in Calling constexpr in default template argument, where the discussion was about point of instantiation of nested instanciation. Here it is more a type problem, but which one ? My function h() returns a reference to a well defined variable of the well defined type (const P &). I expected that some inlining would take place a give the right result but it is not the case. Could you tell me why ?
Declaring the function as inline doesn't change anything to the problem.
Experiments were done with Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn. I also tried with g++-mp-4.8 (MacPorts gcc48 4.8.3_2) 4.8.3 and the error was reported as:
'h()' is not a valid template argument for type 'const P&' because it is not an object with external linkage
It looks like my call to h() (which is a constexpr so compile-time computable) is not seen as such...
I forgot to say that the problem is the same if we try with another reference like this:
const P &pp = p;
and then
C<pp> oo;
this time the first compiler says:
non-type template argument of reference type 'const P &' is not an object
and the second:
error: could not convert template argument 'pp' to 'const P &'
pp is not an object? pp is not of type const P&? Well I can use it as is it one... I know it is a reference but indistinguishable from a native reference, or ?
It looks like this restriction was subject to the following proposal Allow constant evaluation for all non-type template arguments, still trying to determine the status of this proposal. It says:
The syntactic restrictions for pointers, references, and pointers to
members are awkward and prevent reasonable refactorings. For instance:
template<int *p> struct A {};
int n;
A<&n> a; // ok
constexpr int *p() { return &n; }
A<p()> b; // error
and further says:
The historical reason for the restriction was most likely that C++
previously did not have a sufficiently strong specification for
constant expressions of pointer, reference, or pointer-to-member type.
However, that is no longer the case. The status quo is that an
implementation is required to evaluate such a template argument, but
must then discard the result if it turns out to not be null.
In addition to the above, the restriction to entities with linkage is
an artifact of exported templates, and could have been removed when
the linkage restrictions on template type parameters were removed.
and it would remove this section of the note with this restriction:
unnamed lvalues, and named lvalues with no linkage
the whole note reads:
Temporaries, unnamed lvalues, and named lvalues with no linkage are
not acceptable template-arguments when the corresponding
template-parameter has reference type.
Update
The revised version of this proposal N4268 was adopted into the working draft at Urbana and we can see the changes in the latest working draft N4296. The new note reads:
A temporary object is not an acceptable template-argument when the
corresponding template-parameter has reference type
The normative section is 14.3.2 (temp.arg.nontype) paragraph 1 which with this proposal would say:
For a non-type template-parameter of reference or pointer type, the
value of the constant expression shall not refer to (or for a pointer
type, shall not be the address of):
a subobject (1.8),
a temporary object (12.2),
a string literal (2.14.5),
the result of a typeid expression (5.2.8), or
a predefined func variable (8.4.1).
and we can find this new wording in the latest draft standard N4296.
It looks like this change has actually been implemented in clang HEAD see your code working live, using the -std=c++1z flag. This implies the change should be part of C++17, assuming no subsequent changes reverses or alters it.
In the case of
C<h()> oo;
§14.3.2/4 kicks in:
[Note: Temporaries, unnamed lvalues, and named lvalues with no linkage are not acceptable template-
arguments when the corresponding template-parameter has reference type.
(emphasis mine)
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 ]
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.