c++visual-studio-2017array-initialization

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?


Solution

  • 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