3

This is a follow-up to my previous question: C++ compile error constructing object with rvalue std::string from which I learned about the Most Vexing Parse.

I understand now the gist of the problem, however there's one leftover item of syntax I still don't quite understand, which I'd like to ask as a standalone question, since the discussions on the previous post were getting quite long.

Given this code:

#include <iostream>
#include <string>

class Foo
{
    public:
        Foo(double d)
            : mD(d)
        {
        }

        Foo(const std::string& str)
        {
            try
            {
                mD = std::stod(str);
            }
            catch (...)
            {
                throw;
            }
        }

        Foo(const Foo& other)
            : mD(other.mD)
        {
        }

        virtual ~Foo() {}

    protected:
        double mD;
};

class Bar
{
    public:
        Bar(const Foo& a, const Foo& b)
            : mA(a)
            , mB(b)
        {
        }

        virtual ~Bar() {}

    protected:
        Foo mA;
        Foo mB;
};

int main(int argc, char* argv[])
{
    if (argc < 3) { return 0; }

    Foo a(std::string(argv[1]));
    Foo b(std::string(argv[2]));

    Bar wtf(a, b);
}

I understand, now, that the line Foo a(std::string(argv[1])); can be interpreted as either:

(1) Create a Foo named a with an anonymous std::string that is created with a char*. (My desired interpretation)

or

(2) A declaration (not definition) for a function named a that takes a std::string*.

From answers to the original question, I learned that functions could be declared within the scope of another function. That was new to me, but seems within reason, I can buy it.

What I can't wrap my head around, though, is the interpretation of std::string(argv[1]) as a std::string*.

argv[1] is a char*, so I still don't see why the line isn't interpreted as an anonymous std::string being constructed with a char*. After all, I've used code analogous to the following hundreds of times without ever scrutinizing whether this would result in anything other than the construction of a std::string with its char* constructor:

#include <iostream>
int main()
{
    char* pFoo[] = {"foo"};
    std::string str(pFoo[0]);
    std::cout << str << std::endl;
    return 0;
}

I'm on the cusp of understanding the most vexing parse problem; if someone could further explain this last niggling part, that might help push me over the edge.

Thank you.

Community
  • 1
  • 1
StoneThrow
  • 5,314
  • 4
  • 44
  • 86

1 Answers1

4
Foo a(std::string(argv[1]));

declares a function named a which returns Foo and has one parameter (named argv) of type std::string[1]. Since array function parameters are always replaced with pointer parameters, the actual type of the function parameter becomes std::string*.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • This is what I don't get about the most vexing parse. How is `std::string(argv[1])` equivalent to `std::string argv[1]`? Do the braces change absolutely nothing in this case? Consider a function `int f(float arg)`: you're saying that `int f(float(arg))` is equivalent, but that does not seem to be the case. – Violet Giraffe Aug 19 '16 at 19:28
  • Yes, you can add parentheses around any declarator without changing the meaning. – aschepler Aug 19 '16 at 19:29
  • You must be right, GCC throws no error for `int f(float(arg))`, contrary to what I expected. – Violet Giraffe Aug 19 '16 at 19:32
  • Thank you. I think this is the answer I was looking for. I didn't realize that in a function declaration parentheses around a variable were treated this way. I'm amazed there're still so many subtleties to the C++ language for me to encounter even after all my years working with it. – StoneThrow Aug 19 '16 at 22:04