Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor ambiguity with array initializer

I have an issue in that I cannot use a certain constructor with an array initializer when using VS2017 (C++14, C++17 and ISO latest).

I get an C2397 conversion from 'double' to 'unsigned int' requires a narrowing conversion error when it should be invoking the constructor with a container populated with a single element.

#include <vector>

class obj
{
public:
    obj(const std::vector<double>& values, unsigned int stride)
        : values_(values), stride_(stride)
    {
    }

    obj(unsigned int m, unsigned int n)
        : stride_(n)
    {
    }

private:
    unsigned int stride_;
    std::vector<double> values_;
};

int main(int argc, char** argv)
{
    obj m(1, 1);                // correct constructor called.
    obj mm({ 42.0 }, 1);        // Error C2397

    return 0;
}

I can fix this by explicitly declaraing the container...

    obj mm(std::vector<double>({ 42.0 }), 1);

Or initializing the container with more than one item...

    obj mm({ 42.0, 12.0 }, 1);

The latter obviously being of no use, and the former being slightly annoying as it's a corner case for containers with a single item (albeit not the end of the world). I thought this might be problematic for doubles only (having no literal declaration), however it even occurs for floats when initializing them with literals too. i.e container is std::vector<float>, the following line still errors with C2397.

    obj mm({ 42.0f }, 1);

I don't tend to believe in compiler bugs myself having not come across many (although they obviously exist), however I can't help but think this may be one, or if not, is there any mention in the standard how to deal with this situation. Ideally I would like to be able to use the array initializer without explicitly declaring the container type as I can when more than one item exists in the container. Is this possible?

like image 663
lfgtm Avatar asked Mar 02 '23 20:03

lfgtm


2 Answers

Using {{ and }} is the fix in all cases

obj mm({{ 42.0 }}, 1); 

and

obj mm({{ 42.0, 12.0 }}, 1);

Although of course there is no ambiguity in the second case (the use of single braces is exploiting brace-elision).

This question affords a good introduction to the topic: Brace elision in std::array initialization

like image 96
Bathsheba Avatar answered Mar 05 '23 19:03

Bathsheba


Do you mean the following

obj mm({ 1, 42.0 }, 1);

or the following

obj mm({ { 42.0 } }, 1);
like image 27
Vlad from Moscow Avatar answered Mar 05 '23 18:03

Vlad from Moscow