1

I have the following code:

#include <iostream>
#include <array>

typedef std::array<int, 2> eval_t;
eval_t operator*(eval_t e1, eval_t e2) { return {e1[0] * e2[0], e1[1] * e2[1]}; }

int main()
{
    eval_t a = {1, 2};
    eval_t b = a * {2, 1};
    std::cout << "b = (" << b[0] << ',' << b[1] << ')' << std::endl;
}

GCC refused to compile my multiplication:

$ g++ -std=c++11 test.cc 
test.cc: In function ‘int main()’:
test.cc:10:17: error: expected primary-expression before ‘{’ token
  eval_t b = a * {2, 1};
                 ^

I was naively hoping that the only possible operator*() taking eval_t as left operand, would be the one I defined, and the right operand would be understood as eval_t.

Instead, if I write:

eval_t a = {1, 2};
eval_t v = {2, 1};
eval_t b = a * v;

it works.

Steephen
  • 14,645
  • 7
  • 40
  • 47
lucasart
  • 383
  • 1
  • 5
  • 11
  • 1
    Your operator doesn't even take an `eval_t` to multiply it by, but Clang gives a clear error regardless: *error: initializer list cannot be used on the right hand side of operator '\*'* – chris Jun 20 '15 at 22:49

3 Answers3

3

std::array is an aggregate and it doesn't have a constructor accepting std::initializer_list as argument like other containers, so a program can't create an eval_t from an initializer_list. Thus, the compiler cannot find a matching type of the operator overloaded function, so it failed.

For this reason, {1, 2} can't be implicitly converted to an eval_t as you expected.

Pure braced-initializers are not allowed with binary operators as per standard, so compiler will reject it. But it is allowed in return statements. More details are explained in this answer: Initializer lists and RHS of operators

If you change the statement as follows, it will work

eval_t b = a * eval_t{{2, 1}};
Community
  • 1
  • 1
Steephen
  • 14,645
  • 7
  • 40
  • 47
  • The solution is right but the reason is wrong. Braced-initializers are not allowed as operands to operator functions. http://stackoverflow.com/questions/11420448/initializer-lists-and-rhs-of-operators – David G Jun 20 '15 at 23:37
2

Just a syntax error. Correct the line to these.

return{ { e1[0] * e2[0], e1[1] * e2[1] } };

and

eval_t b = a * eval_t{ { 2, 1 } };

Test:

#include <iostream>
#include <array>

typedef std::array<int, 2> eval_t;
eval_t operator*(eval_t e1, eval_t e2) 
{
    return{ { e1[0] * e2[0], e1[1] * e2[1] } };
}

int main()
{
    eval_t a = { 1, 2 };
    eval_t b = a * eval_t{ { 2, 1 } };
    std::cout << "b = (" << b[0] << ',' << b[1] << ')' << std::endl;
    // b = (2,2)   

}
Acha Bill
  • 1,255
  • 1
  • 7
  • 20
  • Thank you. That works. But are you sure the extra braces are useful? – lucasart Jun 20 '15 at 23:07
  • Yes. That is the proper syntax for compilers that dont support c++0x. For recent compilers with c++0x, `eval_t {1,2}{}` should do the same job. – Acha Bill Jun 20 '15 at 23:10
  • 1
    @jafar: if a compiler doesn't support C++11 it won't like the braces for constructors *at all* (or even `std::array`, for what that matters)... – Matteo Italia Jun 20 '15 at 23:12
  • ok. with `g++ -std=c++11`, it seems that `eval_t{1,2}` is sufficient. and no need for extra braces in the `operator*()`. not sure if it's guaranteed by the standard to work, or if it just happens to work for gcc. – lucasart Jun 20 '15 at 23:13
  • @MatteoItalia correct! – Acha Bill Jun 20 '15 at 23:14
  • @lucasart, Yes, brace elision is a thing. – chris Jun 20 '15 at 23:36
0

You might also consider that instead of defining your own overloaded operator*, to take advantage of the standard library:

std::array<int, 2> a = {1, 2};
std::array<int, 2> b = {2, 1};
std::array<int, 2> c;
std::transform(a.begin(), a.end(), b.begin(), c.begin(), std::multiplies<int>());

LIVE DEMO

101010
  • 41,839
  • 11
  • 94
  • 168