1

I have a class, let's call it Sample with variadic template arguments. This class contains a function run(Args... args). This class also implements a stream operator that calls this function.

The class looks like this:

template<typename ...Args>
class Sample
{
    void run(Args... args)
    {
        // do something
    }

    Sample& operator<<(const tuple<Args...>& args)
    {
        run(unpack_somehow(args)...);
        return *this;
    }
};

Now I want to use the stream operator to concat multiple calls, by passing the arguments via the brace initialization of tuples:

void main()
{
    Sample<int, string, int> s;

    // doesn't work :(
    s << {1, "msg", 2} << {1, "msg", 2};
}

I know I could just write make_tuple(1, "msg", 2) and it would work, but I'm looking for a solution that doesn't need additional function calls like make_tuple.

Is there a way to implement such a feature that I can pass the arguments in braces (or maybe by comma separation by overloading the comma operator)?

YePhIcK
  • 5,816
  • 2
  • 27
  • 52
Timo
  • 9,269
  • 2
  • 28
  • 58
  • 5
    A brace-init list is not an expression. There are a few specific spots in the grammar where it's allowed to appear (e.g. as an argument to a function call; in a `return` statement) - after `<<` operator is not one of those spots. The syntax you show is not valid C++, and can't be made to work. – Igor Tandetnik Feb 16 '17 at 15:38
  • 1
    @IgorTandetnik - What's annoying in this case is that *it is* a function call after all. – StoryTeller - Unslander Monica Feb 16 '17 at 15:40
  • 3
    How about `s.run(1, "msg", 2).run(1, "msg", 2);`? Just make `run` return a reference to `this`. Does it have to be `operator<<`, specifically? – Igor Tandetnik Feb 16 '17 at 15:41
  • @IgorTandetnik That's sad. Thanks for the help anyway. I wanted to use the stream operator for syntactical sugar :) – Timo Feb 16 '17 at 15:41
  • 2
    "Syntactic sugar causes cancer of the semicolon." -- [Alan Perlis](http://www.cs.yale.edu/homes/perlis-alan/quotes.html) – Igor Tandetnik Feb 16 '17 at 15:46
  • @IgorTandetnik ROFLMAO!!! – YePhIcK Feb 16 '17 at 15:50
  • Related to [initializer-lists-and-rhs-of-operators](http://stackoverflow.com/questions/11420448/initializer-lists-and-rhs-of-operators) – Jarod42 Feb 16 '17 at 18:31

2 Answers2

2

When you use the line s << {1, "msg", 2} << {1, "msg", 2}; it does not provide the C++ compiler with enough information to deduce what you mean by those initializer lists.

Unless you give a hint to compiler (use a make_tuple or pass an actual tuple variable) it won't know what you mean and won't be able to call the appropriate operator<<().

Looks like you are out of luck. This cannot be done in the way your question is posted.

YePhIcK
  • 5,816
  • 2
  • 27
  • 52
  • Let's assume I could change the implementation of `Sample::run`. How would I have to modify the code to get it working? – Timo Feb 16 '17 at 15:45
  • It is not the good reason, as `s.operator <<({1, "msg", 2})` works. The problem is that initializer_list cannot be used as left/right side of an operator. – Jarod42 Feb 16 '17 at 18:36
2

Initializer-lists are not enabled on the side of an operator.

They are not enabled on the right-hand side because they are not enabled on the left-hand side, and they are not enabled on the left-hand side because that would have posed too big a challenge for parsers.

As to the reasons for this, a draft/discussion paper N2215 by Stroustrup and Dos Reis from 2007 provides a lot of insight into many of the issues with initializer-lists in various contexts. Specifically, there is a section on binary operators (section 6.2):

Consider more general uses of initializer lists. For example:

v = v+{3,4};
v = {6,7}+v;

When we consider operators as syntactic sugar for functions, we naturally consider the above equivalent to

v = operator+(v,{3,4});
v = operator+({6,7},v);

It is therefore natural to extend the use of initializer lists to expressions. There are many uses where initializer lists combined with operators is a “natural” notation.
However, it is not trivial to write a LR(1) grammar that allows arbitrary use of initializer lists. A block also starts with a { so allowing an initializer list as the first (leftmost) entity of an expression would lead to chaos in the grammar.
It is trivial to allow initializer lists as the right-hand operand of binary operators, in subscripts, and similar isolated parts of the grammar. The real problem is to allow ;a={1,2}+b; as an assignment-statement without also allowing ;{1,2}+b;. We suspect that allowing initializer lists as right-hand, but nor [sic] as left-hand arguments to most operators is too much of a kludge, [...]

Jarod42
  • 203,559
  • 14
  • 181
  • 302