I have an array of strings that must be allocated once and their underlying c_str must remain valid for entire duration of the program.
There's some API that provides info about some arbitrary data types. Could look like this:
// Defined outside my code
#define NUMBER_OF_TYPES 23
const char* getTypeSuffix(int index);
The getTypeSuffix is not constexpr, so this must work at least partially in runtime.
The interface that I must provide:
// Returned pointer must statically allocated (not on stack, not malloc)
const char* getReadableTypeName(int type);
Now my array should have following type:
std::string typeNames[NUMBER_OF_TYPES];
For my purposes, it will be initialized within a wrapper class, right in the constructor:
class MyNames
{
MyNames()
{
for (int i = 0; i < NUMBER_OF_TYPES; ++i)
{
names[i] = std::string("Type ") + getTypeSuffix(i);
}
}
const char* operator[](int type) { return _names[(int)type].c_str(); }
private:
std::string _names[NUMBER_OF_TYPES];
};
This is then used in an singleton-ish kind of way, for example:
const char* getReadableTypeName(int type)
{
static MyNames names;
return names[type];
}
Now what I want to improve is that I can see that the for loop in the constructor could be replaced as such:
MyNames() : _names{std::string("Type ") + getTypeSuffix(0), std::string("Type ") + getTypeSuffix(1), ... , std::string("Type ") + getTypeSuffix(NUMBER_OF_TYPES-1)}
{}
Obviously a pseudocode, but you get the point - the array can be initialized directly, leaving the constructor without body, which is neat. It also means that the array member _names can be const, further enforcing the correct usage of this helper class.
I'm quite sure there would be many other uses to filling an array by expressions in compile time, instead of having loop. I would even suspect that this is something that happens anyway during 03.
Is there a way to write a C++11 style array initializer list that has flexible length and is defined by an expression? Another simple example would be:
constexpr int numberCount = 10;
std::string numbers[] = {std::to_string(1), std::to_string(2), ... , std::to_string(numberCount)};
Again, an expression instead of a loop.
I'm not asking this question because I was trying to drastically improve performance, but because I want to learn about new, neat, features of C++14 and later.
instead of C-array use std::array, then you might write your function to return that std::array and your member can then be const:
std::array<std::string, NUMBER_OF_TYPES> build_names()
{
std::array<std::string, NUMBER_OF_TYPES> names;
for (int i = 0; i < NUMBER_OF_TYPES; ++i)
{
names[i] = std::string("Type ") + getTypeSuffix(i);
}
return names;
}
class MyNames
{
MyNames() : _names(build_names()) {}
const char* operator[](int type) const { return _names[(int)type].c_str(); }
private:
const std::array<std::string, NUMBER_OF_TYPES> _names;
};
Now you have std::array, you might use variadic template instead of loop, something like (std::index_sequence stuff is C++14, but can be implemented in C++11):
template <std::size_t ... Is>
std::array<std::string, sizeof...(Is)> build_names(std::index_sequence<Is...>)
{
return {{ std::string("Type ") + getTypeSuffix(i) }};
}
and then call it:
MyNames() : _names(build_names(std::make_index_sequence<NUMBER_OF_TYPES>())) {}
You can defer to an initialization function:
std::array<std::string, NUMBER_OF_TYPES> initializeNames()
{
std::array<std::string, NUMBER_OF_TYPES> names;
for (int i = 0; i < NUMBER_OF_TYPES; ++i) {
names[i] = std::string("Type ") + getTypeSuffix(i);
}
return names;
}
const char* getReadableTypeName(int type)
{
static auto const names = initializeNames();
return names[type].c_str();
}
which can be an immediately invoked lambda:
static auto const names = []{
std::array<std::string, NUMBER_OF_TYPES> names;
// ...
return names;
}();
or do you really need the array requirement? We're making strings anyway so I don't understand, then you can just use range-v3:
char const* getReadableTypeName(int type) {
static auto const names =
view::iota(0, NUMBER_OF_TYPES)
| view::transform([](int i){ return "Type "s + getTypeSuffix(i); })
| ranges::to<std::vector>();
return names[type].c_str():
}
You can use std::make_integer_sequence and a delegating constructor in C++14 (Implementations of std::make_integer_sequence exist in C++11, so this is not really C++14 specific) to get a template parameter pack of integers
#include <string>
#include <utility>
#define NUMBER_OF_TYPES 23
const char* getTypeSuffix(int index);
class MyNames
{
MyNames() : MyNames(std::make_integer_sequence<int, NUMBER_OF_TYPES>{}) {}
template<int... Indices>
MyNames(std::integer_sequence<int, Indices...>) : _names{ (std::string("Type ") + getTypeSuffix(Indices))... } {}
const char* operator[](int type) { return _names[(int)type].c_str(); }
private:
const std::string _names[NUMBER_OF_TYPES];
};
This means that no strings are being default constructed.
Since you ache to use new features, let's use range-v3 (soon-to-be the ranges library in C++2a) to write some really short code:
const char* getReadableTypeName(int type)
{
static const std::vector<std::string> names =
view::ints(0, 23) | view::transform([](int i) {
return "Type " + std::to_string(i);
});
return names[type].c_str();
}
https://godbolt.org/z/UVoENh
Related
I have a vector of 2N lines where the second half (N lines) is basically the same as the first half but with a single character changed, e.g.:
std::vector<std::string> tests{
// First half of lines with '=' as separator between key and value
"key=value",
...
// Second half of lines with ' ' as separator between key and value
"key value",
...
};
Is there a way to parameterize the separator (i.e., = or ) to avoid repeating the lines during the initialization, using the uniform initialization construct? I wonder if there's a better way than creating it with a for loop.
By reading from documentation, it seems not to be possible.
Thanks
Since this is tagged c++17 and you said the strings are known at compile-time, it is technically possible to perform uniform initialization by leveraging a variadic function-template and unpacking the parameters twice, and this would produce the modified string at compile-time.
The idea, in the simplest form, is to do this:
template <typename...Strings>
auto make_string_vector(const Strings&...strings) -> std::vector<std::string>
{
// Unpacks the strings twice; first unmodified, second with '=' swapped with ' '
return std::vector<std::string>{{
strings..., to_space_string(strings)...
}};
};
Where to_space_string is a class that returns a string-like object, done at compile-time.
To do this, we need to make a simple holder that acts like a string is convertible to std::string_view at compile time. This is to ensure that the string we modify has its own separate lifetime and does not dangle:
// A holder for the data so that we can convert it to a 'std::string' type
template <std::size_t N>
struct static_string {
char data[N];
constexpr operator std::string_view() const noexcept {
return std::string_view{data, N};
}
};
Then all we need is the function that takes a compile-time string (array of chars), copies it into the static_string<N> object, and returns it:
// std::string_view used so that we can do this constexpr
template <std::size_t N>
constexpr auto to_space_string(const char(&string)[N]) -> static_string<N>
{
auto storage = static_string<N>{};
std::transform(&string[0], &string[N], &storage.data[0], [](char c){
if (c == '=') { return ' '; }
return c;
});
return storage;
}
The last needed tweak would be for the initializer list to be a sequence of std::string objects, which we can do with static_casts:
template <typename...Strings>
auto make_string_vector(const Strings&...strings) -> std::vector<std::string>
{
return std::vector<std::string>{{
static_cast<std::string>(strings)...,
static_cast<std::string>(to_space_string(strings))...
}};
};
With this, code like:
auto vec = make_string_view("hello=world", "goodbye=world");
will produce a vector containing
hello=world
goodbye=world
hello world
goodbye world
Live Example
Note:
If we didn't use a static_string or some equivalent and instead used string_view directly, the string would dangle. For example:
template <std::size_t N>
constexpr auto to_space_string(const char(&string)[N]) -> std::string_view
{
char storage[N];
std::transform(&string[0], &string[N], &storage[0], [](char c){
if (c == '=') { return ' '; }
return c;
});
// dangles after being returned!
return std::string_view{&storage[0], N};
}
In the above case, we return a reference to temporary storage storage[N], thus causing a dangling pointer (UB). The static_string creates an object first whose lifetime is passed into the caller (make_string_vector) and then gets converted to a std::string.
If only one = per line is allowed then following can work
std::vector<std::string> init_from_key_eq_val(std::initializer_list<const char *> strings)
{
std::vector<std::string> result;
result.reserve(2 * strings.size());
copy(strings.begin(), strings.end(), back_inserter(result));
transform(strings.begin(), strings.end(), back_inserter(result),
[](const char* str)
{
std::string str_copy(str);
size_t eq_pos = str_copy.find('=');
str_copy[eq_pos] = ' ';
return str_copy;
});
return result;
}
int main()
{
std::vector<std::string> vec = init_from_key_eq_val({"key=value"});
}
From the performance point of view, it is no better than doing it with a loop though
This is a follow up of my question given here. In the end I want to create a constexpr std::array containing text with an appended running index.
I wanted to try a different approach than in the previous question.
Nearly everything, what I do in the below code is constexpr. But maybe, it is simply the old problem of returning a pointer to a no longer existing variable. But, I doubt this.
Please see the following code, where the not working line in function main is marked.
#include <iostream>
#include <algorithm>
#include <iterator>
#include <array>
#include <string>
// Some example text
static constexpr const char BaseString[]{ "text" };
// To create something like "text123" as constexpr
template <const size_t numberToConvert, const char* Text>
class Converter {
public:
// Some helper variables
static constexpr size_t TextLength{ std::char_traits<char>::length(Text) };
static constexpr size_t NumberOfDigits{ ([]() constexpr noexcept {size_t result = 0; int temp = numberToConvert; for (; temp != 0; temp /= 10) ++result; return result; }()) };
static constexpr size_t ArrayLength{ (numberToConvert ? 1u : 2u) + NumberOfDigits + TextLength };
// Here we will build the text
char buf[ArrayLength]{};
// Constructor: Convert number to character digits
constexpr Converter() noexcept {
size_t i{ 0 }; for (; i < TextLength; ++i) buf[i] = Text[i]; // Copy text
if (numberToConvert == 0) buf[i] = '0';
else {
i = NumberOfDigits + TextLength - 1; // Convert number to character digits
int number = numberToConvert; for (; number; number /= 10)
buf[i--] = number % 10 + '0';
}
}
// cast operator
constexpr operator const char* () const noexcept { return buf; }
// For test purposes
constexpr const char* data() const noexcept { return buf; }
};
// Driver program
int main() {
// Temporaray constexprs
constexpr Converter<123, BaseString> conv123{}; // Default construction
constexpr auto conv2 = Converter<2, BaseString>(); // Assign / copy
// Build constexpr std::array and initialize it with constexprs
constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };
// Show that it works
std::copy(convArray1.begin(), convArray1.end(), std::ostream_iterator<const char*>(std::cout, "\n"));
// Does compile, but not work. Array will be initialized with nullptr *******************************************
constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
std::cout << convArray2[0] << '\n' << convArray2[0] << '\n';
return 0;
}
So, I can create constexpr "values" with my templated class. Those values can be used in the "initializer" list for a constexpr std::array. But, if I want to use my class directly in the initializer list, then it compiles, but stores only nullptrs. Output of the program is:
text123
text2
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
Why does this happen? Or, is there a solution?
Compiled with Microsoft Visual Studio Community 2019, Version 16.8.2, C++17, Debug, X86
Your code generating compile time dangling pointers (which should be impossible) on MSVC.
To fix:
template <const size_t numberToConvert, const char* Text>
class Converter {
// blah
std::array<char, ArrayLength> buf{};
constexpr operator std::array<char, ArrayLength>() const { return buf; }
constexpr std::array<char, ArrayLength> get() const { return *this; }
};
and remove other conversion operators and data method.
template<const size_t numberToConvert, const char* Text>
constexpr auto Converted = Converter<numberToConvert, Text>{}.get();
and now use Converted<blah...>.data() to get the pointers you want.
If you really want implicit conversion to character pointer:
template<const size_t numberToConvert, const char* Text>
struct Convertest {
constexpr operator char const*() const { return Converted<numberToConvert,Text>.data(); }
};
rename classes and variables however you like.
constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(),
Converter<2, BaseString>().data() };
Here, you are storing pointers to temporary variables - both Converter objects seize to exist after ;. Making dereferencing the pointers UB.
Clang rejects such code giving quite helpful message:
<source>:51:43: note: pointer to subobject of temporary is not a constant expression
<source>:51:55: note: temporary created here
constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
^
2 errors generated.
Execution build compiler returned: 1
I am not sure about the specific constexpr rules but the code is unsafe even if it would compile.
In Cpp-Reference you can see that
A constant expression is either
[...]
a prvalue core constant expression whose value satisfies the following constraints: [...] if the value is of pointer type, it holds - address of an object with static storage duration
So, for convArray1
constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };
you have to make static conv123 and conv2
// VVVVVV
static constexpr Converter<123, BaseString> conv123{};
static constexpr auto conv2 = Converter<2, BaseString>();
// ^^^^^^
because you can't have a constant expression from a pointer with not static storage.
For convArray2
constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
I don't see a way to get a constexpr object from pointers inside temporary objects.
Consider a class whose default constructor takes in the file path as a parameter.
class Test
{
public:
Test(const std::string& filepath);
...
...
};
Now I wish to create and initialize an array of Test objects using unique pointers in VS2017.
int main()
{
std::unique_ptr<Test[]> m_Tests;
int testCount = 2;
std::string path1, path2;
m_Tests = std::make_unique<Test[]>(testCount); // This line gives a compilation error
m_Tests[0] = std::make_unique<Test>(path1);
m_Tests[1] = std::make_unique<Test>(path2);
}
How can I make this work?
g++ 9.2.0 tells me that you lack default constructor, i.e. one without parameters. Adding such constructor works fine. If it's not what you want, you can create array of unique_ptr's, so std::unique_ptr<std::unique_ptr<Test>[]> and after that initialize each element by hand, something similar to this:
#include <memory>
#include <algorithm>
#include <iostream>
struct Test {
std::string str_;
Test(std::string const& str) : str_(str) { }
void print() { std::cout << str_ << '\n'; }
};
int main()
{
std::unique_ptr<std::unique_ptr<Test>[]> m_Tests;
int testCount = 2;
std::string path1{"a"}, path2{"b"};
m_Tests = std::make_unique<std::unique_ptr<Test>[]>(testCount);
std::array<std::string, 2> paths{path1, path2};
std::transform(paths.begin(), paths.end(), &m_Tests[0],
[](auto const& p) { return std::make_unique<Test>(p); });
for (int i = 0 ; i < testCount ; ++i) {
m_Tests[i]->print();
}
}
There is no overload of std::make_unique that does this, so you would need to use new directly:
m_Tests.reset(new Test[testCount]{path1, path2});
This will however only compile if testCount is a constant expression, so you need to change the definition int testCount = 2; to const int or constexpr int.
If testCount is not a constant expression, there needs to be a default constructor defined for the case that testCount is smaller than 2 at runtime.
So, really, you probably want to ignore testCount and just let the array size be deduced:
m_Tests.reset(new Test[]{path1, path2});
It would be much easier if you just used std::vector:
std::vector<Test> m_Tests;
//...
m_Tests.emplace_back(path1);
m_Tests.emplace_back(path2);
How about you use std::array and can you get rid of testCount (or use it as constexp) then the code can be like below.
class Test
{
public:
Test(const std::string& filepath){}
};
int main()
{
constexpr int testCount = 2;
std::unique_ptr<std::array<Test, testCount>> m_Tests;
std::string path1, path2;
m_Tests = std::make_unique<std::array<Test, testCount>>(std::array<Test, testCount>{path1,path2});
}
The Ghostscript interpreter API has a function
GSDLLEXPORT int GSDLLAPI gsapi_init_with_args(void *instance, int argc, char **argv)
The final argument argv is a pointer to an array of C strings, which are interpreted as command-line arguments. I obviously cannot change the signature of the function gsapi_init_with_args to take a const char ** argument instead.
If I were willing to ignore (or silence) the deprecated conversion from string constant to 'char*' warning, then I would write simply
char *gs_argv[] = {"", "-dNOPAUSE", "-dBATCH", ...};
and pass gs_argv as the final argument. But I would prefer to fix my code so that I am not relying on an external function to behave in the way I expect it to (and effectively treat gs_argv as const char**).
Is there any simple way to declare gs_argv as an array of pointers to (non-const) C strings, and initialize its elements with string literals? (That is, using a similar approach to how I can initialize a single C string: using char c_str[] = "abc".) The best I can think of is to use
const char *gs_argv0[] = {"", "-dNOPAUSE", "-dBATCH", ...};
and then copy the contents, element by element, into gs_argv.
Please note that I understand why the compiler gives this warning (and have read the answers to, among others, this question). I am asking for a solution, rather than an explanation.
You can use:
char arg1[] = "";
char arg2[] = "-dNOPAUSE";
char arg3[] = "-dBATCH";
char* gs_argv0[] = {arg1, arg2, arg3, NULL};
int argc = sizeof(gs_argv0)/sizeof(gs_argv0[0]) - 1;
gsapi_init_with_args(instance, argc, gs_argv0)
Create copies of the string literals using strdup. This is more verbose, but fixes the warning.
char* gs_argv0[NARGS];
gs_argv0[0] = strdup("");
gs_argv0[1] = strdup("-dNOPAUSE");
// ...
Note that you will also need to free the memory allocated by strdup if you want to prevent leaks.
You might also want to add a comment to your code saying why you are doing this, to make it clear for future readers.
If you can guarantee that the function will not modify the non-const parameter, then it is acceptable to use const_cast in this situation.
A C++14 solution.
#define W(x) \
(([](auto& s)->char* \
{ \
static char r[sizeof(s)]; \
strcpy (r, s); \
return r; \
})(x))
char* argv[] =
{ W("--foo=bar",
W("baz"),
nullptr
};
Since this code requires C++11, there's a lower cost C++11 solution in another answer below. I'm leaving this one for posterity.
There are pretty much two choices: ignore it and const_cast, or do the right thing. Since this is modern C++, you're supposed to have nice, RAII classes. Thus, the simplest, safest thing to do is to safely wrap such an array.
// https://github.com/KubaO/stackoverflown/tree/master/questions/args-cstrings-32484688
#include <initializer_list>
#include <type_traits>
#include <cstdlib>
#include <cassert>
#include <vector>
class Args {
struct str_vector : std::vector<char*> {
~str_vector() { for (auto str : *this) free(str); }
} m_data;
void append_copy(const char * s) {
assert(s);
auto copy = strdup(s);
if (copy) m_data.push_back(copy); else throw std::bad_alloc();
}
public:
Args(std::initializer_list<const char*> l) {
for (auto str : l) append_copy(str);
m_data.push_back(nullptr);
}
template <std::size_t N>
Args(const char * const (&l)[N]) {
for (auto str : l) append_copy(str);
m_data.push_back(nullptr);
}
/// Initializes the arguments with a null-terminated array of strings.
template<class C, typename = typename std::enable_if<std::is_same<C, char const**>::value>::type>
Args(C l) {
while (*l) append_copy(*l++);
m_data.push_back(nullptr);
}
/// Initializes the arguments with an array of strings with given number of elements.
Args(const char ** l, size_t count) {
while (count--) append_copy(*l++);
m_data.push_back(nullptr);
}
Args(Args && o) = default;
Args(const Args &) = delete;
size_t size() const { return m_data.size() - 1; }
char ** data() { return m_data.data(); }
bool operator==(const Args & o) const {
if (size() != o.size()) return false;
for (size_t i = 0; i < size(); ++i)
if (strcmp(m_data[i], o.m_data[i]) != 0) return false;
return true;
}
};
Let's see how it works:
#include <iostream>
extern "C" int gsapi_init_with_args(void*, int argc, char** argv) {
for (int i = 0; i < argc; ++i)
std::cout << "arg " << i << "=" << argv[i] << std::endl;
return 0;
}
int main()
{
Args args1 { "foo", "bar", "baz" };
const char * args2i[] { "foo", "bar", "baz", nullptr };
Args args2 { (const char **)args2i };
const char * args3i[] { "foo", "bar", "baz" };
Args args3 { args3i };
const char * const args4i[] { "foo", "bar", "baz" };
Args args4 { args4i };
const char * args5i[] { "foo", "bar", "baz" };
Args args5 { args5i, sizeof(args5i)/sizeof(args5i[0]) };
assert(args1 == args2);
assert(args2 == args3);
assert(args3 == args4);
assert(args4 == args5);
gsapi_init_with_args(nullptr, args1.size(), args1.data());
}
Output:
arg 0=foo
arg 1=bar
arg 2=baz
Try to const_cast it:
gsapi_init_with_args(instance, argc, const_cast<char**>(argv));
Maybe it will help with fixing warning.
Inspired by n.m.'s C++14 version, here's a C++11 version. The trick is to use an evaluated empty lambda expression to generate a fresh type, so that each instantiation of W__ is unique.
template <typename T, int N> static char * W__(const char (&src)[N], T) {
static char storage[N];
strcpy(storage, src);
return storage;
}
#define W(x) W__(x, []{})
char * argv[] = {
W("foo"),
W("bar")
};
The static in front of W__'s return type means that W__ has internal linkage and won't bloat the object file with extra symbols. It has nothing to do with the static in front of storage, as the latter indicates the static storage duration for the local variable. The code below would be perfectly valid, but of course doing the wrong thing and having undefined behavior:
template <typename T, int N> static char * BAD(const char (&src)[N], T) {
char storage[N];
strcpy(storage, src);
return storage;
}
Since a lambda has to be evaluated, you can't simply make its type a template argument:
template<typename> void G();
G<decltype([]{})>(); // doesn't work
I have written some code to cast const char* to int by using constexpr and thus I can use a const char* as a template argument. Here is the code:
#include <iostream>
class conststr
{
public:
template<std::size_t N>
constexpr conststr(const char(&STR)[N])
:string(STR), size(N-1)
{}
constexpr conststr(const char* STR, std::size_t N)
:string(STR), size(N)
{}
constexpr char operator[](std::size_t n)
{
return n < size ? string[n] : 0;
}
constexpr std::size_t get_size()
{
return size;
}
constexpr const char* get_string()
{
return string;
}
//This method is related with Fowler–Noll–Vo hash function
constexpr unsigned hash(int n=0, unsigned h=2166136261)
{
return n == size ? h : hash(n+1,(h * 16777619) ^ (string[n]));
}
private:
const char* string;
std::size_t size;
};
// output function that requires a compile-time constant, for testing
template<int N> struct OUT
{
OUT() { std::cout << N << '\n'; }
};
int constexpr operator "" _const(const char* str, size_t sz)
{
return conststr(str,sz).hash();
}
int main()
{
OUT<"A dummy string"_const> out;
OUT<"A very long template parameter as a const char*"_const> out2;
}
In this example code, type of out is OUT<1494474505> and type of out2 is OUT<106227495>. Magic behind this code is conststr::hash() it is a constexpr recursion that uses FNV Hash function. And thus it creates an integral hash for const char* which is hopefully a unique one.
I have some questions about this method:
Is this a safe approach to use? Or can this approach be an evil in a specific use?
Can you write a better hash function that creates different integer for each string without being limited to a number of chars? (in my method, the length is long enough)
Can you write a code that implicitly casts const char* to int constexpr via conststr and thus we will not need aesthetically ugly (and also time consumer) _const user-defined string literal? For example OUT<"String"> will be legal (and cast "String" to integer).
Any help will be appreciated, thanks a lot.
Although your method is very interesting, it is not really a way to pass a string literal as a template argument. In fact, it is a generator of template argument based on string literal, which is not the same: you cannot retrieve string from hashed_string... It kinda defeats the whole interest of string literals in templates.
EDIT : the following was right when the hash used was the weighted sum of the letters, which is not the case after the edit of the OP.
You can also have problems with your hash function, as stated by mitchnull's answer. This may be another big problem with your method, the collisions. For example:
// Both outputs 3721
OUT<"0 silent"_const> out;
OUT<"7 listen"_const> out2;
As far as I know, you cannot pass a string literal in a template argument straightforwardly in the current standard. However, you can "fake" it. Here's what I use in general:
struct string_holder //
{ // All of this can be heavily optimized by
static const char* asString() // the compiler. It is also easy to generate
{ // with a macro.
return "Hello world!"; //
} //
}; //
Then, I pass the "fake string literal" via a type argument:
template<typename str>
struct out
{
out()
{
std::cout << str::asString() << "\n";
}
};
EDIT2: you said in the comments you used this to distinguish between several specializations of a class template. The method you showed is valid for that, but you can also use tags:
// tags
struct myTag {};
struct Long {};
struct Float {};
// class template
template<typename tag>
struct Integer
{
// ...
};
template<> struct Integer<Long> { /* ... */ };
// use
Integer<Long> ...; // those are 2
Integer<Float> ...; // different types
Here is the pattern that I am using for template const string parameters.
class F {
static constexpr const char conststr[]= "some const string";
TemplateObject<conststr> instance;
};
see :
https://stackoverflow.com/a/18031951/782168