Templated constructor
I recently faced the following issue.
I wrote a class with a constructor with a template non-type parameter, and realized I could not call the constructor in any way.
A common use-case for a template non-type parameter is if one wants to always be able to access the content at compile-time, for example with a static_assert.
The code can be simplified to the following example:
struct s0 {
int i;
};
struct s1 {
template <s0 input>
constexpr s1() {
static_assert(input.i > 5);
// ... do other things
}
};
constexpr auto v = s1<s0{10}>();The line where the variable v is created leads to an error similar to "s1 is not a template", "no matching function for call to 's1::s1()'", "expected '(' for function-style cast or type construction", and "syntax error: unexpected token '<' following 'simple-type-specifier'".
Constructors are special functions; it is not possible to take their address or call them directly:
struct s2 {
s2() = default;
};
auto v = s2::s2();The example fails to compile with "cannot call constructor 's2::s2' directly" with GCC, and with "error: qualified reference to 's2' is a constructor name rather than a type in this context" with clang. The Microsoft compiler does not diagnose anything and accepts the example code as valid.
The solution, as often, is an indirection:
struct s0 {
int i;
};
template<s0 v>
struct value_t {};
struct s3 {
template <s0 input>
constexpr s3( value_t< input > ) {
static_assert(input.i > 5);
// ... do other things
}
};
constexpr auto v = s3(value_t<s0{10}>());In this case, we do not need to define the template when calling the constructor; the template parameter does the job for us.
The syntax value_t<s0{10}>() is valid, because this code is not trying to call a constructor template, but it instantiates a template class with its default (non-template) constructor.
Another possible solution is another type of indirection: define a factory function: Although it might not always be a viable alternative, depending on what the constructor of the class is doing.
struct s0 {
int i;
};
struct s4 {
constexpr s4(s0) {
// ... do other things
}
template <s0 input>
constexpr static s4 make(){
static_assert(input.i > 5);
return s4(input);
}
};
constexpr auto v = s4::make<s0{10}>();The main advantage of this method is that there is no need to define a separate type (value_t), which makes the construction slightly more verbose (s3(value_t<s0{10}>()) vs s4::make<s0{10}>()).
If you have questions, comments, or found typos, the notes are not clear, or there are some errors; then just contact me.