Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perfect forwarding fails when target is aggregate with array

#include <iostream>

struct X2
{
    int i;
    int j;
    char buf[10];
};

X2 glob{1,2,"abc"};    // OK

struct X
{
     X2 x2;

     template<typename... Args>
     X(Args&&... args): x2{args...} {}
};

int main()
{
     X x;                // OK
     X y{1, 2};          // OK
     X z{1, 2, "abc"};   // error
}

The last line gives error: 17 : error: invalid conversion from 'const char*' to 'char' [-fpermissive]

If I use std::forward(args)... instead of args... then even more errors come up; and also there are errors if I try to use {'a', 'b', 'c', '\0'} as initializer instead of the string literal.

Is there a way to make this work, i.e. allow X z{......}; where anything inside the braces that would be a legal initializer for x2, is accepted and does in fact initialize x2 ?

like image 665
M.M Avatar asked Dec 19 '15 05:12

M.M


1 Answers

This is a precarious design issue that was inherited from C++98: Certain conversions or initializations are syntactically restricted to literals, in particular string literals as initializers for char arrays ([dcl.init.string]/1) and integer literals as null pointer constants ([conv.ptr]/1). That, of course, doesn't go well with "perfect" forwarding.

For null pointers, the issue was circumvented by introducing nullptr, which can be used instead of 0 and works fine even after being forwarded.

In your case, there are basically two major options:

  • Exploit brace elision - X is an aggregate, too:

    struct X {
         X2 x2;
    } z{1, 2, "abc"}; // Ok
    
  • Declare buf to have class type, e.g. std::string or, perhaps more appropriate in your case, some statically sized equivalent (limited to size 10).

like image 108
Columbo Avatar answered Oct 17 '22 05:10

Columbo