2

I got a simple CRTP template class that I've been playing with and I came up to a point that I need to have the same ctor but essentially do different things at the constructor initialization list.

I do have an alternative solution but I was wondering if what I wanted to do could be achieved in a better way.

A strip down version of the class is pasted below. You can ignore the Eigen stuff its just a matrix library.

template<class ImplT, size_t N, bool SquareMatrix = false>
class Foo
{
  template<bool Test, typename Then, typename Else>
  using conditional_t = typename std::conditional<Test, Then, Else>::type;

  // this is defined at compile time so no need to init it
  using VecN = Eigen::Matrix<double, N, 1>;

  // this is a dynamic matrix so dimensions need to be specified at runtime
  using MatN = Eigen::MatrixXd;
  using MatrixType = conditional_t<SquarePreCovariance, MatN, VecN>;

  inline ImplT& impl() noexcept             { return static_cast<ImplT&>(*this); }
  inline const ImplT& impl() const noexcept { return static_cast<const ImplT&>(*this); }

  public:
  // if SquareMatrix == false
  Foo()
    : parameters3(Matrix(N, N))
  {}

  // if SquareMatrix == true
  Foo()
    : parameters2(Matrix(N, N)), 
      parameters3(Matrix(N, N)),
    {}

  // easy solution that covers all cases
  Foo()
    : parameters3(Matrix(N, N)), 
  {
    if (SquareMatrix)
        parameters2.resize(N,N);
  }

private:
  VecN parameters;
  MatrixType parameters2;
  MatN  parameters3;
};
Cœur
  • 37,241
  • 25
  • 195
  • 267
xerion
  • 303
  • 2
  • 11
  • I think you'll have to specialize for both cases. This normally generates a lot of boilerplate code, so you might be able to make use of C++11's constructor inheritance feature (http://stackoverflow.com/questions/9979194/what-is-constructor-inheritance) to make it a little more elegant. (Not tested, so I hope I'm making sense.) Edit: forget it, the answers so far are much better. – JorenHeit Jan 12 '15 at 12:25

2 Answers2

11

What about conditionally delegating constructors?

private:

    // if SquareMatrix == false
    Foo(std::false_type)
      : parameters3(Matrix(N, N))
    {}

    // if SquareMatrix == true
    Foo(std::true_type)
      : parameters2(Matrix(N, N)), 
        parameters3(Matrix(N, N))
      {}

public:

    Foo() : Foo(std::integral_constant<bool, SquareMatrix>{})  {}
Columbo
  • 60,038
  • 8
  • 155
  • 203
  • 2
    Tag dispatching > specializations (usualy) +1 – jrok Jan 12 '15 at 12:29
  • What are the performance implications of Tag Dispatching (TD) vs SFINAE vs the simple "if()" and resize the matrices, considering that I might have more than just 2 to init. I can understand that with "if()" I will pay the price of first constructing the matrices and then resizing them while in the other cases I can skip that, but what about TD implementation cost? I assume SFINAE will be the best in terms of doing exactly what I want apart from some duplication due to the 2 ctors. I can see that the simple if generated the smaller assembly code (in debug) but that is all I can say. – xerion Jan 13 '15 at 07:13
7

Constructors can be templated, so long as all template arguments can be deduced or have defaults.

Substitution failures for template arguments cause it to be skipped during overload resolution.

Care must be taken to make it clear that the two constructor versions are valid overloads, and an additional dummy template parameter can take care of that.

#include <utility>
#include <iostream>

template <bool B>
struct S {
  template <bool B_ = B, typename = typename std::enable_if<!B_>::type>
  S() { std::cout << "1" << std::endl; }

  template <bool B_ = B, typename = typename std::enable_if<B_>::type, typename = void>
  S() { std::cout << "2" << std::endl; }
};

int main() {
  S<false> sf; // okay, prints 1
  S<true> st; // okay, prints 2
}
  • I missed your response, was there any interesting point you made? :o) – Columbo Jan 12 '15 at 12:38
  • @Columbo Nah, just that your approach would've worked, but I was already working on a different approach when you commented. :) –  Jan 12 '15 at 12:39
  • @hvd This approach works equally well with Columbo's apart from the many compiler warning messages about multiple defined default constructors. Essentially being flooded with multiple warnings for each derived class. Definitely interesting to know about the dummy template parameter. – xerion Jan 12 '15 at 22:50
  • @xerion gcc and clang don't give any warnings, and that's what I used for testing, but I now see that VC++ does indeed give warnings. It really shouldn't, the code is perfectly valid, and the documentation for the warning does not even match the actual compiler behaviour: ["The class has multiple default constructors. The first constructor is used."](http://msdn.microsoft.com/en-us/library/f7fwa979.aspx) That's incorrect, even on VC++ the second constructor is correctly invoked for `S`. Oh well. You could suppress the warning project-wide, but now it's easier to use Columbo's answer. –  Jan 13 '15 at 07:50
  • @hvd That is what I did but only for that header fileand it worked fine. I would not want to do it project wide. – xerion Jan 13 '15 at 07:53