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?
A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument.
#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.
Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression.
A constexpr function that is eligible to be evaluated at compile-time will only be evaluated at compile-time if the return value is used where a constant expression is required. Otherwise, compile-time evaluation is not guaranteed.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With