1

I have a class with the following constructors:

template<typename T>
class MyClass {
public:
    MyClass() = default;
    explicit MyClass(T val) : value_1(val) { /* ... */ }
    explicit MyClass(T val, T val2) : value_1(val), value_2(val2) { /* ... */}

private:
    T value_1 = 0;
    T value_2 = 0;
};

I also want to create a constructor that takes an arbitrary number of arguments (all of which can be T's). The constructor also mustn't shadow the other constructors I already wrote, as the work done is different. I tried around with enable_if a little bit, but am unable to get this to work. Here is what I have so far:

template<typename... TArgs>
explicit MyClass(TArgs... mArgs, typename std::enable_if<sizeof...(mArgs) >= 3>) { /* ... */ }

However, when called like this:

MyClass<double>(2, 3, 4, 5, 6, 7);

it yields this error (among others):

error: no matching function for call to 'MyClass<double>::MyClass(int, int, int, int, int, int)'

So the compiler probably doesn't even see the constructor. On the other hand if I just leave out the enable_if, it will never call my other, more specialized constructors.

In short, how do I make it so the variadic constructor is called if and only if there are three or more arguments given to the constructor?

EDIT:

As proposed in the comments, I have now also tried the following, which also does not work:

template<typename... TArgs>
explicit MyClass(TArgs... mArgs, typename std::enable_if<sizeof...(mArgs) >= 3>::type) { /* ... */ }

as well as

template<typename... TArgs>
explicit MyClass(TArgs... mArgs, typename std::enable_if<sizeof...(mArgs) >= 3>* = nullptr) { /* ... */ }

or any combination of the two.

max66
  • 65,235
  • 10
  • 71
  • 111
L. Kue
  • 473
  • 5
  • 19
  • Possible duplicate : [How to properly use std::enable_if on a constructor](https://stackoverflow.com/questions/49286989/how-to-properly-use-stdenable-if-on-a-constructor) – François Andrieux Jan 25 '19 at 20:30
  • 1
    I'm not sure if this is the problem, but your `enable_if` should be either `std::enable_if_t` (if using C++14), or should be using `typename std::enable_if<...>::type`. Without the `::type`, you have nothing to SFINAE on. – Human-Compiler Jan 25 '19 at 20:32
  • @FrançoisAndrieux No. I've tried the proposed solution from that question and it still yields the same error as I described in my question. And it is not possible for me to put the `enable_if` in the template declaration (that is, before the arguments), due me needing to access mArgs. – L. Kue Jan 25 '19 at 20:33
  • @L.Kue If you've tried putting `std::enable_if` as a template argument and it didn't work you should show your attempt here. – François Andrieux Jan 25 '19 at 20:36
  • @Bitwize I've updated my question. – L. Kue Jan 25 '19 at 20:36
  • It's unclear what your problem is. variadic template constructor will have lower priority, so proper constructor should be picked up without any SFINAE tricks. TLDR: MCVE. – SergeyA Jan 25 '19 at 20:43

1 Answers1

3

Suggestion: try with

template <typename... TArgs,
          typename = typename std::enable_if<sizeof...(TArgs) >= 3>::type>
explicit MyClass(TArgs... mArgs) { /* ... */ }

or better (to avoid collisions with multiple SFINAE enabled constructors with the same signature).

template <typename... TArgs,
          typename std::enable_if<sizeof...(TArgs) >= 3, bool>::type = true>
explicit MyClass(TArgs... mArgs) { /* ... */ }

The problem with your original code

template<typename... TArgs>
explicit MyClass(TArgs... mArgs, typename std::enable_if<sizeof...(mArgs) >= 3>::type) { /* ... */ }

is that you can deduce a variadic list of types only if the relative arguments are in last position.

If you add another argument

typename std::enable_if<sizeof...(mArgs) >= 3>::type

you break the deduction for the TArgs... types.

max66
  • 65,235
  • 10
  • 71
  • 111
  • This appears to be the correct solution. I was unaware you could call `sizeof...()` on a template parameter type! – L. Kue Jan 25 '19 at 20:40