Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I prevent user from specifying a function template parameter, forcing it to be deduced?

Tags:

c++

Let's say I have a template function:

template <typename A, typename B>
A fancy_cast(B)
{
    return {};
}

The intended usage is something like fancy_cast<int>(1.f).

But nothing stops the user from specifying the second template parameter manually: fancy_cast<int, int>(1.f), which would cause problems.

How can I prevent typename B from being specified and force it to be deduced?

I've come up with this:

// Using this wrapper prevents the code from being
// ill-formed NDR due to [temp.res]/8.3
template <auto V> inline constexpr auto constant_value = V;

template <
    typename A,
    typename ...Dummy,
    typename B,
    typename = std::enable_if_t<constant_value<sizeof...(Dummy)> == 0>
>
A fancy_cast(B)
{
    return {};
}

It appears to work, but it's extremely cumbersome. Is there a better way?

like image 878
HolyBlackCat Avatar asked Nov 08 '19 16:11

HolyBlackCat


2 Answers

What about making fancy_cast a variable template?

template <typename A>
struct fancy_cast_t {
    template <typename B>
    A operator()(B x) const { return x; }
};

template <typename A>
constexpr fancy_cast_t<A> fancy_cast {};

fancy_cast<int>(1.5);  // works
fancy_cast<int, int>(1.5);  // doesn't work
fancy_cast<int>.operator()<int>(1.5);  // works, but no one would do this
like image 107
Brian Bi Avatar answered Oct 17 '22 08:10

Brian Bi


This is not the most efficient solution, but you can create a class that has a template parameter for the type to convert to, and then have a constructor template that takes any type. Then if you add an operator T for the type you instantiate the class with you can have that return correct value. That would look like

template<typename T>
struct fancy_cast
{
    T ret;
    template<typename U>
    fancy_cast(U u) : ret(u) {} // or whatever you want to do to convert U to T
    operator T() && { return std::move(ret); }
};

int main()
{
    double a = 0;
    int b = fancy_cast<int>(a);
}

This works because there is no way to specify the template parameter for the constructor since you can't actually call it.

like image 3
NathanOliver Avatar answered Oct 17 '22 06:10

NathanOliver