4
votes

I'm using Visual Studio 2010 and have factory for creating one of two implementations of an abstract base class. The factory Create method takes a bool flag and returns one of the two impls in a shared_ptr. Using an if statement works fine for me, but the compiler complains when I try to use a ternary with make_shared calls.

class Base {
public:
   Base() {};
};

class Foo : public Base {
public:
    Foo() {};
};

class Bar : public Base {
public:
    Bar() {};
};

class Factory {
public:
    static std::shared_ptr<Base> Create(bool isFoo) {
        return isFoo ?
            std::make_shared<Foo>() :
            std::make_shared<Bar>();
    }
};

int main() {
    std::shared_ptr<Base> instance = Factory::Create(true);
    return 0;
}

The error VS is giving is 'No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called c:\path\file.h(78): error C2668: 'std::tr1::shared_ptr<_Ty>::shared_ptr' : ambiguous call to overloaded function with [ _Ty=Base ]

Note that

static std::shared_ptr<Base> Create(bool isFoo) {
    if (isFoo)
        return std::make_shared<Foo>();
    return std::make_shared<Bar>();
}

compiles just fine.

4

4 Answers

7
votes

It's because both the second and third expression in a ternary operation must be convertible to each other. In your case std::shared_ptr<Foo> is simply not convertible to std::shared_ptr<Bar>, and the other way around.

While they both can be converted to std::shared_ptr<Base> they are not convertible to each other.

3
votes

When you have two different class types as the second and third operands to the ternary conditional operator, in general it must be possible to convert one of them to the other. If neither can be converted to the other, the program won't compile, even if there's a third type that both can be converted to, as in this case.

Solution:

return isFoo ?
    std::shared_ptr<Base>(std::make_shared<Foo>()) :
    std::shared_ptr<Base>(std::make_shared<Bar>());
1
votes

The return type of std::make_shared<T>() is std::shared_ptr<T>.

Both deriveds are convertible to their base-class, and thus both shared_ptrs can be converted to shared_ptr<base>, but the ternary operator expects its second and third parameter to already have the same type, or exactly one of them to be convertible to the other.

0
votes

As your note seems to indicate, the issue is with the ternary operator. Every expression (even things like a=1) has a type in c++. In your example, the compiler has to try to deduce a type for the expression isFoo ? std::make_shared<Foo>() : std::make_shared<Bar>(); Normally the compiler looks for a type that works for both the second and third operands (the make_shared calls in this case). What VS is trying to tell you in its error message is that it failed to find a common type.

In contrast, the if statement works because the make_shared calls are two separate expressions, so a common type isn't needed.