1

I'm fairly new to template metaprogramming and have been working through some of the concepts - however, I've been stumped a little by this particular snippet I came across.

template<class TAG, typename... DATATYPES>
struct Message {

    Message (typename std::enable_if<sizeof...(DATATYPES) >= 1>) {
    }

    ... (various other constructor declarations here)

    std::tuple<DATATYPES...> m_data;
};

I had assumed when reading it that it was enabling the default constructor if there were one or more DATATYPES arguments, but having tested it all I got was a compilation error.

I would appreciate any assistance in helping my understanding of this snippet as I understand what enable_if is supposed to be doing but within this context I don't seem to be able to wrap my head around what's actually happening.

EDIT: I guess this is less a question of 'how do I achieve this particular effect?' and more along the lines of 'what is this code actually producing, and does it match up with what I understood to be the intention of the original author?'

GMemory
  • 137
  • 9
  • I don't have the time to post a complete solution, but have a look at the answer for http://stackoverflow.com/questions/21901637/class-template-why-cant-i-specialize-a-single-method-for-void-type/21904225#21904225, the same techniques can be applied to your problem, except that instead of using `IsVoid`, you would define something based on the number of arg types. – Martin J. Feb 24 '14 at 11:47
  • 1
    As a rule of thumb, if you see `enable_if` in a function that is not a template in its own right (i.e. members of class templates don't count), it's wrong. – Sebastian Redl Feb 24 '14 at 13:34

2 Answers2

4

std::enable_if is not used correctly if not followed by ::type. std::enable_if<expr> itself is a fairly useless struct type.

A correct way of conditionally enabling the default constructor:

template<class TAG, typename... DATATYPES>
struct Message {
private:
    struct dummy_type {};
public:
    template <typename T = std::tuple<DATATYPES...>>
    Message(
        typename std::enable_if<std::tuple_size<T>() >= 1, dummy_type>::type
        = dummy_type{}
    ) {}
    //...
};

Live at coliru.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • It will not work, unfortunately. The constructor itself needs to be a template. – jrok Feb 24 '14 at 11:54
  • Is what it's doing at the moment is creating a (useless) constructor with a struct arg if the enable_if condition evaluates to true? – GMemory Feb 24 '14 at 11:56
  • Got it fixed now. The original code is creating a useless constructor with struct arg no matter how many arguments there are. – aschepler Feb 24 '14 at 12:09
  • I see, thanks. I'm guessing that the original author didn't intend for that to be the case. Since this is a snippet from a framework that is intended to be made public I'll try to let the author know. – GMemory Feb 24 '14 at 12:11
1

Member function signatures are a part of class definition and need to be resolved when the class is instantiated. This means that compiler tries enable_if, too, and if the conditon is not fullfilled, it discovers that it doesn't have the nested type - hard error.

To make SFINAE work, you need to make the constructor a template and have enable_if depend on a template parameter. See @acheplers's answer for example.

What the code in OP does is a weird way of asserting the size of DATATYPE pack, which would be done with static_assert much more clearly. Or, maybe the author just didn't know how to do SFINAE correctly.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • Thanks for your insights, much appreciated. I had a gut feeling that the code wasn't doing what was intended of it but I lack the firm grasp on the concepts to be able to tell for sure. – GMemory Feb 24 '14 at 12:19