std::make_shared as a default argument does not compile
Asked Answered
L

2

4

In Visual C++ (2008 and 2010), the following code does not compile with the following error:

#include <memory>

void Foo( std::shared_ptr< int >    test = ::std::make_shared< int >( 5 ) )
{
}

class P
{
    void
    Foo( std::shared_ptr< int > test = ::std::make_shared< int >( 5 ) )
    {
    }
};

error C2039: 'make_shared' : is not a member of '`global namespace''

error C3861: 'make_shared': identifier not found

It is complaining about the definition of P::Foo() not ::Foo().

Does anybody know why it is valid for Foo() to have a default argument with std::make_shared but not P::Foo()?

Lenticel answered 7/5, 2010 at 13:14 Comment(4)
Note that in Visual C++ 2008, shared_ptr is in std::tr1, and make_shared is not implemented (make_shared was not part of TR1).Schreibman
sounds like you do ::make_sharedCob
Ignore the 2008 bit, I'm confusing the IDE with the compiler.Lenticel
I can reproduce the problem in VS2010. I switched it to use boost::shared_ptr and it won't compile in VS2008. It complains that it "could not deduce template argument for 'T'," which does not make any sense to me, because you explicitly specify T (int).Schreibman
S
6

It looks like a bug in the compiler. Here is the minimal code required to reproduce the problem:

namespace ns
{
    template <typename T>
    class test
    {
    };

    template <typename T>
    test<T> func()
    {
        return test<T>();
    }
}

// Works:
void f(ns::test<int> = ns::func<int>()) { }

class test2
{
    // Doesn't work:
    void g(ns::test<int> = ns::func<int>()) 
    { 
    }
};

Visual C++ 2008 and 2010 both report:

error C2783: 'ns::test<T> ns::func(void)' : could not deduce template argument for 'T'

Comeau has no issues with this code.

Schreibman answered 7/5, 2010 at 14:16 Comment(1)
I submitted a defect report on Microsoft Connect: connect.microsoft.com/VisualStudio/feedback/details/557653Schreibman
N
0

I hit what appears to be the same issue in my own code. The minimal code I boiled it down to was this:

namespace N
{
    template<typename T>
    T defaultValue()
    {
        return T();
    }

    template<typename T>
    void fun( const T& value = N::defaultValue<T>() ){}
}

int main(int argc, char* argv[])
{
    N::fun<int>();
    return 0;
}

This is slightly different to James McNellis example - and, I think, highlights the fact that it is the namespace qualification in the default argument initiliser where it goes wrong.

In this case defaultValue and fun are in the same namespace, so you can trivially remove N:: from N::defaultValue and it works.

If defaultValue is in a different namespace you can still workaround it by either bringing it into the local namespace with using, or writing a local forwarding template function, e.g.:

namespace N1
{
    template<typename T>
    T defaultValue()
    {
        return T();
    }
}
namespace N2
{
    template<typename T>
    T defaultValueFwd()
    {
        return N1::defaultValue<T>();
    }

    template<typename T>
    void fun( const T& value = defaultValueFwd<T>() ){}
}

int main(int argc, char* argv[])
{
    N2::fun<int>();
    return 0;
}

A bit of a pain, but workable. I believe you could use this technique in the make_shared case, although I haven't tried it.

Novelize answered 6/5, 2011 at 11:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.