9

I'm trying to pass a literal string as a template parameter in a C++14 project. Google told me that I can do as below:

struct Test {
    static const char teststr[];

    template<const char* str>
    void p() {std::cout << str;}
};

const char Test::teststr[] = "Hello world!";

int main() {
    Test t;
    t.p<Test::teststr>();
}

It did work.

However, if I use const char*, instead of const char []. It won't work.

struct Test {
    static const char* teststr;

    template<const char* str>
    void p() {std::cout << str;}
};

const char* Test::teststr = "Hello world!";

int main() {
    Test t;
    t.p<Test::teststr>();
}

Now it doesn't work. The compiler told me that 'Test::teststr' is not a valid template argument because 'Test::teststr' is a variable, not the address of a variable.

Well, I don't know what it meant.

1

3 Answers 3

8

The error message from the compiler is clear enough:

error: 'Test::teststr' is not a valid template argument because 'Test::teststr' is a variable, not the address of a variable

So you need:

#include <iostream>

struct Test {
    static const char* teststr;

    template<const char **str>
    void p() {std::cout << *str;}
};

const char* Test::teststr = "Hello world!";

int main() {
    Test t;
    t.p <&Test::teststr>();
}

And then it works - the point being that [the contents of] a variable is not a compile-time constant, whereas the address of a variable (if it's a static or global variable) is.

Sign up to request clarification or add additional context in comments.

7 Comments

Interesting: If you wanted to use it with an array, as in the OP's use case from google, you'd have to introduce an in-between static helper pointer variable, initialize it with the array's first element's address and and pass its address as template param: const char Test::teststr[] = "Hello world!"; const char *testStrPtr = Test::teststr; /* or ... = &Test::teststr[0]; */and in main: t.p <&Test::teststrPtr>();.
@Peter-ReinstateMonica That's not the only way
@Peter-ReinstateMonica Well yes, that was my intention.
I suppose the OP would like to have a single function usable with both variants (as would be the case with non-template arguments where arrays are adjusted to pointers when they appear as arguments).
|
7

This is just according to the rules of c++:

Template non-type arguments.
For pointers to objects, the template arguments have to designate the address of a complete object with static storage duration and a linkage (either internal or external)

https://en.cppreference.com/w/cpp/language/template_parameters

A global array of characters has linkage while a string literal doesn’t.
In C++20 this has been changed and you can use string literals as template parameters.

1 Comment

const char* isn't a string literal, it is a pointer to const char. Thus, your answer is incorrect because you said: "A global array of characters has linkage while a string literal doesn’t." but this doesn't apply to teststr as it(teststr) is a const char* and not a string literal.
3

The problem is that in case 2 of your example the static data member teststr is a pointer type that has the address of a string literal which is not allowed by the standard as can be seen from the quoted statement below ,

From non-type template parameter's documentation:

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.1) a subobject (1.8),

  • (1.2) a temporary object (12.2),

  • (1.3) a string literal (2.13.5),

Thus, in case 2 of your example teststr cannot be used as it is a pointer type that has the address of the string literal "Hello world!".


4 Comments

And I suppose the reason to forbid string literals is that they may or may not be the same objects, and the compiler wouldn't know how to compare for equality. (Two identical literals may trigger two different instantiations.)
The accepted answer isn't incorrect it just presents an arguably weird solution while not providing the reason why in full. Your answer isn't full either because even if that restriction wasn't present the OP's code would still be incorrect because they have non-constant pointer. So your quote had not chance to be applied yet.
@ixSci Even if the teststr was const/constexpr the program wouldn't work. My point is that OP asked: "I don't know what it meant." and the accepted answer only repeated the question instead of answering the question. Moreover the reason given there isn't correct either. Because even if you make testptr a constexpr the program will still fail and the reason is as specified in my answer. That answer ignores the underlying issue which isn't just that testptr is not a compile time constant but also that the standard prohibits such usage even if it was a compile time constant.
Your concerns are valid but they don't make the answer wrong. They made illegitimate code legitimate by working around the underlying issue w/o mentioning it entirely! So their answer is technically correct, although the OP hardly learned anything from it. On the other hand you are talking about string literals while the OP didn't get to them yet. So the OP had to add const (or constexpr) to the pointer to be stricken by the quote you cited. So yes, the underlying issue is that literals can't be used literally but the immediate issue is different.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.