user2183336
user2183336

Reputation: 655

Constexpr alias of overloaded template function

Trying to alias make_shared on a specific class type for a specific constructor of that class. My best attempt:

class foo { public: foo(int x) : y(x) {} int y; };
constexpr auto newfoo = static_cast<std::shared_ptr<foo>(*)(int)>(std::make_shared<foo>);

Yields:

error: invalid static_cast from type ‘<unresolved overloaded function type>’ to type ‘std::shared_ptr<foo> (*)(int)’
constexpr auto newfoo = static_cast<std::shared_ptr<foo>(*)(int)>(std::make_shared<foo>);

What am I doing wrong?

Upvotes: 8

Views: 619

Answers (1)

Vittorio Romeo
Vittorio Romeo

Reputation: 93324

std::make_shared is a variadic function template. You are only specifying <foo> as a template parameter, but you would also need an int somewhere in there. Regardless, your approach is bound to fail as it's reliant on how make_shared's template arguments were laid out and because it's generally cumbersome to work with overload sets in C++.

What I suggest is to create a wrapper function instead:

constexpr auto newfoo(int x)
{
    return std::make_shared<foo>(x);
}

In my opinion it is easier to write, read, and understand. If you really need SFINAE-friendliness and noexcept, you can repeat the body three times:

constexpr auto newfoo(int x) 
    ->          decltype(std::make_shared<foo>(x))
       noexcept(noexcept(std::make_shared<foo>(x)))
      {           return std::make_shared<foo>(x); }

A macro can be used to make the above declaration less painful.


If you really want a function pointer, this seems to work:

auto newfoo = 
    static_cast<std::shared_ptr<foo>(*)(const int&)>(
        &std::make_shared<foo, const int&>);

Look at make_shared's declaration:

template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );

You need to supply T=foo and something for Args.... Since Args... is a forwarding reference pack, it will always either deduce to lvalue references or rvalue references. This is why <foo, const int&> is a valid set of template parameters and <foo, int> is not.

As Zefick pointed out in the comments, all of this can be simplified to:

constexpr auto newfoo = &std::make_shared<foo, const int&>;

The cast is not really needed here.

Upvotes: 4

Related Questions