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)
Related
I am learning C++ using the books listed here. In particular, I learnt that we cannot use std::string as a non-type template parameter. Now, to further clear my concept of the subject I tried the following example which compiles in gcc and msvc but not in clang. Demo
std::string nameOk[] = {"name1", "name2"};
template<std::string &name>
void foo()
{
}
int main()
{
foo<nameOk[0]>(); //this compiles in gcc and msvc but not in clang in C++20
}
My question is which compiler is right here(if any). That is, is the program well-formed or IFNDR.
Clang is complaining that your template argument is a subobject. (If you make the argument a complete string object, it works.)
This behavior is based on an earlier restriction in the standard at [temp.arg.nontype], which read
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 (6.7.2 [intro.object]),
This restriction is lifted as of P1907 which is in C++20, but Clang hasn't reflected that yet. GCC also fails when you use e.g. version 10 with C++17:
error: '& nameOk[0]' is not a valid template argument of type 'std::string&' {aka 'std::__cxx11::basic_string<char>&'} because 'nameOk[0]' is not a variable
Clang is wrong in rejecting the code as this is allowed by the standard. From temp.param#6:
6) A non-type template-parameter shall have one of the following (possibly cv-qualified) types:
6.1) a structural type (see below),
7) A structural type is one of the following:
7.2) an lvalue reference type, or
(emphasis mine)
This means that std::string& can be used as a nontype template parameter for which nameOk[0] is a valid nontype template argument and the program is well-formed.
I don't really understand why the code below does not compile:
template<const char*>
struct Foo{};
constexpr const char s1[] = "test1";
constexpr const char* const s2 = "test2";
int main()
{
Foo<s1> foo1; // ok
// Foo<s2> foo2; // doesn't compile
}
Uncommenting the last line in main() makes g++ and clang++ emit the errors
error: 's2' is not a valid template argument because 's2' is a
variable, not the address of a variable
and
error: non-type template argument for template parameter of
pointer type 'const char *' must have its address taken
respectively.
My questions are:
Why is s1 instantiation OK and s2 not?
Is there any sane situation where such pointer non-type template parameter is of any use?
In a comment above, vsoftco adds:
seems extremely weird, afaik string literals are not temporaries but are stored for the whole duration of the program, so their address is for sure a compile time constant (or at least that's what I believe)
That's true. However, the standard doesn't specify whether string literals have unique addresses.
Some linkers merge or deduplicate string literals. I have worked on systems where "ello" == "hello"+1 actually evaluates to true. Other linkers are so dumb that "hello" in foo.cc has a different address from "hello" in bar.cc. Heck, some tiny C compilers are so dumb that "hello" can have two different addresses within the same translation unit!
For such a dumb linker (or compiler), should Foo<"hello"> cause one instantiation or two? That is...
const char *sa = "hello world";
const char *sb = "hello world";
assert(sa != sb); // this assertion is permitted to succeed
template<char*> struct F {};
F<"hello world"> fa;
F<"hello world"> fb;
assert(!is_same<decltype(fa), decltype(fb)>::value);
// should we permit this assertion to succeed also?
The Committee admirably refused to open that can of worms, by simply prohibiting the construct.
Now, it's conceivable (to me, at the moment) that sometime in the future the Committee could mandate that all string literals be deduplicated by the same mechanism that implementations currently use for inline and template functions. That is, we can imagine a source-level transformation that turns
const char *sc = "yoo hoo";
into
inline auto& __stringlit_yoo_x20hoo() {
static const char x[] = "yoo hoo";
return x;
}
const char *sc = __stringlit_yoo_x20hoo();
Then there would be only a single instance of __stringlit_yoo_x20hoo (and only a single instance of that function's static array x) anywhere in the program, so the meaning of F<"yoo hoo"> would be unambiguous. The implementation would have to name-mangle the thing unambiguously as well, but that's a simple problem once you've already committed to name-mangling things like F<1+1> and F<FruitType,ORANGE> (which C++ compilers have been doing forever).
...But then you would still have problems with those extremely smart linkers (like the one I worked on) that allow
assert("hello" == "hello\0world"); // this assertion is permitted to succeed
assert(!is_same_v< F<"hello">, F<"hello\0world"> >);
// should we permit this assertion to succeed also?
// Surely this way lies madness.
For 1.:
From [temp.arg.nontype]
1 A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter. 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):
[...]
(1.3) — a string literal (2.13.5),
s2 holds the address of a string literal, and so cannot be used as the parameter here. s1 on the other hand is an array of char that has been initialized with a string literal, but the value of s1 (when converted to const char*) doesn't point to the string literal used in the initialization.
For 2.:
Function pointers perhaps? Still I can't say I've ever used a pointer as a non-type parameter.
There's a recent change in the relevant standard text, but the code is acceptable in neither version of the standard.
N4140 [temp.arg.nontype]/p1:
1 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
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of a complete object with static storage duration and external or internal
linkage or a function with external or internal linkage, including
function templates and function template-ids but excluding
non-static class members, expressed (ignoring parentheses) as & id-expression,
where the id-expression is the name of an object or
function, except that the & may be omitted if the name refers to a
function or array and shall be omitted if the corresponding
template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
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; or
a constant expression of type std::nullptr_t.
N4296 [temp.arg.nontype]/p1:
A template-argument for a non-type template-parameter shall be a
converted constant expression (5.20) of the type of the
template-parameter. 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.13.5),
the result of a typeid expression (5.2.8), or
a predefined __func__ variable (8.4.1).
The N4140 version is the one currently implemented by compilers. The N4296 version is somewhat more relaxed, but in neither case is the address of a string literal an acceptable template argument.
Presumably part of the reason for this is that template arguments must be mangled, and mangling a string literal in a sane way that would work across multiple translation units would be very difficult, if not impossible.
I don't really understand why the code below does not compile:
template<const char*>
struct Foo{};
constexpr const char s1[] = "test1";
constexpr const char* const s2 = "test2";
int main()
{
Foo<s1> foo1; // ok
// Foo<s2> foo2; // doesn't compile
}
Uncommenting the last line in main() makes g++ and clang++ emit the errors
error: 's2' is not a valid template argument because 's2' is a
variable, not the address of a variable
and
error: non-type template argument for template parameter of
pointer type 'const char *' must have its address taken
respectively.
My questions are:
Why is s1 instantiation OK and s2 not?
Is there any sane situation where such pointer non-type template parameter is of any use?
In a comment above, vsoftco adds:
seems extremely weird, afaik string literals are not temporaries but are stored for the whole duration of the program, so their address is for sure a compile time constant (or at least that's what I believe)
That's true. However, the standard doesn't specify whether string literals have unique addresses.
Some linkers merge or deduplicate string literals. I have worked on systems where "ello" == "hello"+1 actually evaluates to true. Other linkers are so dumb that "hello" in foo.cc has a different address from "hello" in bar.cc. Heck, some tiny C compilers are so dumb that "hello" can have two different addresses within the same translation unit!
For such a dumb linker (or compiler), should Foo<"hello"> cause one instantiation or two? That is...
const char *sa = "hello world";
const char *sb = "hello world";
assert(sa != sb); // this assertion is permitted to succeed
template<char*> struct F {};
F<"hello world"> fa;
F<"hello world"> fb;
assert(!is_same<decltype(fa), decltype(fb)>::value);
// should we permit this assertion to succeed also?
The Committee admirably refused to open that can of worms, by simply prohibiting the construct.
Now, it's conceivable (to me, at the moment) that sometime in the future the Committee could mandate that all string literals be deduplicated by the same mechanism that implementations currently use for inline and template functions. That is, we can imagine a source-level transformation that turns
const char *sc = "yoo hoo";
into
inline auto& __stringlit_yoo_x20hoo() {
static const char x[] = "yoo hoo";
return x;
}
const char *sc = __stringlit_yoo_x20hoo();
Then there would be only a single instance of __stringlit_yoo_x20hoo (and only a single instance of that function's static array x) anywhere in the program, so the meaning of F<"yoo hoo"> would be unambiguous. The implementation would have to name-mangle the thing unambiguously as well, but that's a simple problem once you've already committed to name-mangling things like F<1+1> and F<FruitType,ORANGE> (which C++ compilers have been doing forever).
...But then you would still have problems with those extremely smart linkers (like the one I worked on) that allow
assert("hello" == "hello\0world"); // this assertion is permitted to succeed
assert(!is_same_v< F<"hello">, F<"hello\0world"> >);
// should we permit this assertion to succeed also?
// Surely this way lies madness.
For 1.:
From [temp.arg.nontype]
1 A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter. 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):
[...]
(1.3) — a string literal (2.13.5),
s2 holds the address of a string literal, and so cannot be used as the parameter here. s1 on the other hand is an array of char that has been initialized with a string literal, but the value of s1 (when converted to const char*) doesn't point to the string literal used in the initialization.
For 2.:
Function pointers perhaps? Still I can't say I've ever used a pointer as a non-type parameter.
There's a recent change in the relevant standard text, but the code is acceptable in neither version of the standard.
N4140 [temp.arg.nontype]/p1:
1 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
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of a complete object with static storage duration and external or internal
linkage or a function with external or internal linkage, including
function templates and function template-ids but excluding
non-static class members, expressed (ignoring parentheses) as & id-expression,
where the id-expression is the name of an object or
function, except that the & may be omitted if the name refers to a
function or array and shall be omitted if the corresponding
template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
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; or
a constant expression of type std::nullptr_t.
N4296 [temp.arg.nontype]/p1:
A template-argument for a non-type template-parameter shall be a
converted constant expression (5.20) of the type of the
template-parameter. 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.13.5),
the result of a typeid expression (5.2.8), or
a predefined __func__ variable (8.4.1).
The N4140 version is the one currently implemented by compilers. The N4296 version is somewhat more relaxed, but in neither case is the address of a string literal an acceptable template argument.
Presumably part of the reason for this is that template arguments must be mangled, and mangling a string literal in a sane way that would work across multiple translation units would be very difficult, if not impossible.
I've been experimenting with function types in C++. Note that I don't mean pointer-to-function types like:
typedef void (*voidFuncPtr)();
but the more exotic:
typedef void (voidFunc)();
I didn't expect the following code to compile, but surprisingly it did:
template<voidFunc func>
class funcClass
{
public:
void call() { func(); };
};
void func()
{ }
void Test()
{
funcClass<func> foobar;
foobar.call();
}
however, if I try adding the following to funcClass:
voidFuncPtr get() { return &func; }
I get the error Address expression must be an lvalue or a function designator
My first question here is: what kind of black magic is the compiler using to pretend that a func type is something it can actually pass around an instance of? Is it just treating it like a reference? Second question is: if it can even be called, why can't the address of it be taken? Also, what are these non-pointer-to function types called? I only discovered them because of boost::function, and have never been able to find any documentation about them.
§14.1.4 of the Standard says:
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, [this is what yours is]
— lvalue reference to object or lvalue reference to function,
— pointer to member,
— std::nullptr_t.
And §14.1.6 says
A non-type non-reference template-parameter is a prvalue. It shall not
be assigned to or in any other way have its value changed. A non-type
non-reference template-parameter cannot have its address taken. When a
non-type non-reference template-parameter is used as an initializer
for a reference, a temporary is always used.
So that explains the two behaviours you are seeing.
Note that func is the same as &func (§14.3.2.1):
[A non-type template parameter can be] a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a
function with external or internal linkage, including function
templates and function template-ids but excluding non-static class
members, expressed (ignoring parentheses) as & id-expression, except
that the & may be omitted if the name refers to a function or array
and shall be omitted if the corresponding template-parameter is a
reference; or...
So it's just a function pointer.
Given that the code compiles without the address-of operator and pointers (including to functions and member functions) are valid template arguments, it seems the compiler considers voidFunc to be a function pointer type, i.e., the decayed version of the type. The rules for this didn't change between C++ 2003 and C++ 2011.
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.