Why can't a pointer to __func__ be a template argument? - c++

Why I can't pass this pointer as a template argument:
template <const char* T>
struct MyStruct
{};
int main()
{
static constexpr const char* func_name = nullptr;
MyStruct<func_name>{}; // Works
static constexpr const char* func_name2 = __func__;
MyStruct<func_name2>{};
// A template argument may not reference a non-external entity
// MyStruct: invalid expression as a template argument for 'T'
//error: the address of ‘__func__’ is not a valid template argument
}
I want to pass the name of the function as a template argument, and no workaround seems possible. Why?
According to the GCC documentation:
The identifier __func__ is implicitly declared by the translator as if, immediately following the opening brace of each function definition, the declaration
static const char __func__[] = "function-name";
So it's as if it's declaring that inside the function, then I assign my constexpr const char* to it, and then pass it as the template argument. That should work, does it not?
I know that there are gotchas when it comes to const char* as a template argument. Before I was having trouble with passing a string literal (they're not allowed as template arguments). But in this case it seems I'm passing the address of a static array, which is a valid argument, no?

__func__ is explicitly disallowed.
[temp.arg.nontype]
3 For a non-type template-parameter of reference or pointer type, or for each non-static data member of reference or pointer type in a non-type template-parameter of class type or subobject thereof, the reference or pointer value shall not refer to or be the address of (respectively):
[...]
a predefined __func__ variable ([dcl.fct.def.general]), or
[...]
So GCC is correct.
You should also note that the GCC documentation states it is "as if" there is such a variable. There doesn't have to be one.
The content and exact whereabouts of __func__ is implementation defined, so just like string literals it is not constrained enough to play well with the one-definition rule when determining the linkage of templated entities.

Related

Is it possible to use a pointer as a template argument? [duplicate]

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.

Cannot convert template argument NULL to void*

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.

pointer non-type template parameter

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.

Why doesn't const A & bind to A() in template arguments?

I'm trying to do this template instantiation but it's not working. I'm getting the errors:
prog.cpp:7:15: error: template-id 'f<const A&, A()>' for 'void f()' does not match any template declaration
template <class T, T> void f() {}
struct A {};
template void f<const A &, A()>();
int main() {}
This is weird, because when I do it in main it works:
int main() {
const A &a = A(); // no error
}
So why doesn't it work in the template line?
Possible duplicate of Non-type template parameters
These are the rules of template non type parameters
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.
What you are passing is an RValue (temporary object, etc which cannot be assigned to), which does not fall under any of these possibilities.
edit:
It appears that it is infact being interpreted as a function type, but your template signature expects a non type parameter of type A (exactly a const A&)
A template argument cannot be a temporary object. Only primitive types which can reasonably be compared for exact equality may be template non-type arguments. This includes
integers,
enumerators, and
pointers to objects with extern linkage.
But
floating-point numbers aren't allowed because they can be very close yet not equal
static objects might have the same name but different locations in different files, which would make the template-id confusingly resolve to different instantiations with the same name in different files
same goes for string literals
temporary objects don't have consistent addresses so you can't pass a pointer to one
the value of a temporary object as you passed, which can't even be tested for equality, would never let the language match one template instantiation to another!
(As Pubby notes, A() is actually interpreted as the type of a function with no parameters returning A. So the compiler is just failing to find a template declaration taking two type parameters.)

const variable as non-type template parameter (VARIABLE cannot appear in a constant-expression)

Why does this work?
char __nontype[] = "foo";
typedef TemplateClass<T, __nontype> MyClass;
But this (with a constant variable) not?
const char __nontype[] = "foo";
typedef TemplateClass<T, __nontype> MyClass;
Compiler Error:
error: ‘__nontype’ cannot appear in a constant-expression
error: template argument 2 is invalid
The difference is because const affects the linkage. It works if you add extern. That said, as far as I can tell:
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:
an integral constant expression (including a constant expression of literal class type that can be used as an integral constant expression as described in 5.19); or
the name of a non-type template-parameter; or
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
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.
it should also work without extern. The object is allowed to have internal linkage, but your compiler does not yet support that. This is one of the changes in C++11, the previous C++ standard did not allow it.
The error says it: the result is not a constant expression (it is known at link time, but not compile time).
Here is an example that would work:
typedef const char *nontype_t;
template <nontype_t> struct Y {};
char hello[] = "hello";
constexpr char* world = hello;
int main()
{
Y<hello> a;
}