C++ logo
The C++ logo, by Jeremy Kratz, licensed under CC0 1.0 Universal Public Domain Dedication

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.