SFINAE in variadic constructor - c++

I want to define a generic strong alias type, i.e. a type
template<typename T, auto ID = 0>
class StrongAlias {
T value;
};
such that for a type T a StrongAlias<T> can be used in exactly the same way as T, but StrongAlias<T, 0> and StrongAlias<T, 1> are different types that can not be implecitly converted to each other.
In order to mimic a T as perfectly as possible, I would like my StrongAlias to have the same constructors as T.
This means I would like to do something like the following:
template<typename T, auto ID = 0>
class StrongAlias {
T value;
public:
// doesn't work
template<typename... Args, typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
StrongAlias(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
: value(std::forward<Args>(args)...) {}
};
except that this wouldn't work since the template parameter pack must be the last template parameter, as clang 5.0 would tell me.
The other way to use SFINAE that I thought of would be in the return type, but since a constructor doesn't have a return type, this does not seem to work either.
Is there any way to use SFINAE on a variadic template parameter pack in a constructor?
Alternatively, if there isn't one, can I accomplish what I want in another way?
Note that being implicitly constructible from a T isn't enough in my case, as the example of StrongAlias<std::optional<int>> shows: If StrongAlias can only be implictly constructed from a std::optional<int>, it cannot be be constructed from a std::nullopt (of type std::nullopt_t), because that would involve 2 user-defined conversions. I really want to have all constructors of the aliased type.
EDIT:
Of course it would be possible to implement this without SFINAE and let the program be invalid if a StrongAlias is constructed from incompatible arguments. However, while this would be an acceptable behaviour in my specific case, it is clearly not optimal as the StrongAlias may be used in a template that queries if the given type is constructible from some arguments (via std::is_constructible). While this would yield a std::false_type for T, it would result in a std::true_type for StrongAlias<T>, which could mean unnecessary compile errors for StrongAlias<T> that wouldn't exist for T.

Just change std::enable_if_t to a non-type template parameter:
template<typename T, auto ID = 0>
class StrongAlias {
T value;
public:
template<typename... Args, std::enable_if_t<std::is_constructible_v<T, Args...>, int> = 0>
StrongAlias(Args&&... args) noexcept(noexcept(T(std::declval<Args>()...)))
: value(std::forward<Args>(args)...) {}
};

The two issues with your snippet that make it not compile are
Trying to pass a type std::is_constructible<T, Args...> as the first argument of std::enable_if_t;
Trying to pass decltype(...) to a noexcept operator.
(There's also a third problem with the function style cast inside the noexcept, but that only affects semantics, not compilability.)
Neither causes the error message you cite, which concerns a rule that doesn't apply to function templates at all. With these two problems fixed, Wandbox's Clang 5.0 happily accepts it.

Related

Contradictory SFINAE on constructor using std::is_constructible

Why is the following code behaving as commented?
struct S
{
template<typename T, typename = std::enable_if_t<!std::is_constructible_v<S, T>>>
S(T&&){}
};
int main() {
S s1{1}; // OK
int i = 1;
S s2{i}; // OK
static_assert(std::is_constructible_v<S, int>); // ERROR (any compiler)
}
I get that for the constructor to be enabled, the assertion must be false. But S still is constructed from int in the example above! What does the standard say and what does the compiler do?
I would assume that before enabling the template constructor, S in not constructible so std::is_constructible<S, int> instantiates to false. That enables the template constructor but also condemns std::is_constructible<S, int> to always test false.
I've also tested with my own (pseudo?) version of std::is_constructible:
#include <type_traits>
template<typename, typename T, typename... ARGS>
constexpr bool isConstructibleImpl = false;
template<typename T, typename... ARGS>
constexpr bool isConstructibleImpl<
std::void_t<decltype(T(std::declval<ARGS>()...))>,
T, ARGS...> =
true;
template<typename T, typename... ARGS>
constexpr bool isConstructible_v = isConstructibleImpl<void, T, ARGS...>;
struct S
{
template<typename T, typename = std::enable_if_t<!isConstructible_v<S, T>>>
S(T&&){}
};
int main() {
S s1{1}; // OK
int i = 1;
S s2{i}; // OK
static_assert(std::is_constructible_v<S, int>); // OK
}
I suppose that it is because now std::is_constructible is not sacrificed for SFINAE in the constructor. isConstructible is sacrificed instead.
That brings me to a second question: Is this last example a good way to perform SFINAE on a constructor without corrupting std::is_constructible?
Rational: I ended up trying that SFINAE pattern to tell the compiler not to use the template constructor if any of the other available constructors matches (especially the default ones), even imperfectly (e.g., a const & parameter should match a & argument and the template constructor should not be considered a better match).
Your first code example is undefined behaviour, because S is not a complete type within the declaration of itself. std::is_constructible_v however requires all involved types to be complete:
See these paragraphs from cppreference.com:
T and all types in the parameter pack Args shall each be a complete
type, (possibly cv-qualified) void, or an array of unknown bound.
Otherwise, the behavior is undefined.
If an instantiation of a template above depends, directly or
indirectly, on an incomplete type, and that instantiation could yield
a different result if that type were hypothetically completed, the
behavior is undefined.
This makes sense because in order for the compiler to know if some type can be constructed it needs to know the full definition. In your first example, the code is kind of recursive: the compiler needs to find out if S can be constructed from T by checking the constructors of S which themselves depend on is_constructible etc.

Template parameter pack of mixed templated types (std::function recreation with optional arguments)

I'm attempting to make an std::function alternative that supports optional arguments with defaults. I tried a few different syntactical ideas, but the most realistic seems to be a parameter pack of specialised templates that hold argument data. Here's my desired outer syntax:
Function<
void /*Return type*/,
Arg<false, int> /*Required int parameter*/,
Arg<true, bool, true> /*Optional bool parameter that defaults to true*/
> func;
I would have liked to maintain the Function<ReturnType(args)> syntax but it appears you can only put typenames in parentheses and not classes. Here's my current code:
template<bool Optional, typename Type, Type Default>
class Arg;
template<typename Type, Type Default>
class Arg<false, Type, Default> {};
template<typename Type, Type Default>
class Arg<true, Type, Default> {};
Problem 1: I can't find a way to make the "Default" parameter non-required for the false specialisation. I've tried proxy classes with a default constructor, changing the third argument to a pointer and with specifying nullptr (which isn't really ideal syntactically), and a const reference to a type (which still requires three arguments from the user side) but nothing seems to allow two arguments to be accepted with Function<false, Type>.
Problem 2: I can't find the right syntax to get a parameter pack of mixed template argument types. I've tried (obviously invalid syntax)
template<typename RetType, Arg<bool, typename Type, Type>... args>
class Function{};
and even a double/nested template but I can't make this work either.
All of this indirection really steams from the fact that you can't have multiple parameter packs in a class template so I have to find creative ways to specify optional arguments, but I will take any solution I can get to somehow making this function at compile-time and not having to construct these functions dynamically.
I'm using C++20.
To make the third template argument optional when the first argument is false, you can use a default argument with a std::enable_if:
template <bool Optional, typename T,
T Default = std::enable_if_t<!Optional, T>{}>
class Arg;
This way, Arg<false, int> is equivalent to Arg<false, int, 0>, whereas Arg<true, int> is ill-formed.
You can use generic arguments:
template <typename R, typename... Args>
class Function {
static_assert(std::conjunction_v<is_arg_v<Args>...>);
};
Where is_arg can be something as simple as
template <typename T>
struct is_arg :std::false_type {};
template <bool Optional, typename T, T Default>
struct is_arg<Arg<Optional, T, Default>> :std::true_type {};
template <typename T>
inline constexpr bool is_arg_v = is_arg<T>::value;

experimental make_array, can I use brace-init list as arguments?

I tried to implement a version of make_array that is not exactly the same, but very similar to:
http://en.cppreference.com/w/cpp/experimental/make_array
I found that this code seems not to work, am I right about this?
using MyType = std::pair<int, float>;
constexpr auto the_array = make_array<MyType>({1, 7.5f});
The problem here seems to be that, even if make_array specifies explicitly the first template parameter, the arguments still come from a parameter pack,
which is unrelated to the explicitly specified argument.
This means that the code will not work.
Is there any solution that can make the code above work?
My version
template <class Val = void, class... Args,
class = std::enable_if_t<std::is_void<Val>{}, int>>
constexpr std::array<
std::tuple_element_t
<0,
std::tuple<Args...>>,
sizeof...(Args)> makeArray(Args &&... args) {
return {{std::forward<Args>(args)...}};
}
template <class Val = void, class... Args,
class = std::enable_if_t<!std::is_void<Val>{}, int>>
constexpr std::array<
Val,
sizeof...(Args)> makeArray(Args &&... args) {
return {{std::forward<Val>(args)...}};
}
make_array is a variadic template function which takes arguments of any type. It will then perform implicit conversion on these parameters to get to the array's base type. As such, unless you specify every argument's type, the compiler will have to use template argument deduction.
And braced-init-lists cannot be deduced (outside of auto variables). So you will have to explicitly mention the type name when constructing each member.

Variadic template that determines the best conversion

How can we implement a variadic template that, given a type T and a list of types E1, E2, ... EN, determines the type of that list for which the conversion from T to that type is, according to overload resolution, the best?
void should be the output if no best conversion exists - in other words, when either there is an ambiguity or T cannot be converted to any type in the list. Note that this implies that our template should be SFINAE-friendly, i.e. not hard-error when no best conversion exists.
The following static_asserts should succeed:
static_assert( std::is_same< best<int, long, short>, void >{}, "" );
static_assert( std::is_same< best<int, long, std::string>, long >{}, "" );
static_assert( std::is_same< best<int>, void >{}, "" );
(Assuming, for simplicity, that best is an alias template referring to the actual template)
This case is left unspecified:
static_assert( std::is_same< best<int, int, int>, ???>{}, "" );
Either void or int should be acceptable here. (If the latter is chosen then we can still check in a wrapper template whether the result type is contained twice in the list, and if it is, output void instead).
My currently best approach:
#include <type_traits>
template <class T> using eval = typename T::type;
template <class T> struct identity {using type = T;};
template <typename T, typename... E>
class best_conversion
{
template <typename...> struct overloads {};
template <typename U, typename... Rest>
struct overloads<U, Rest...> :
overloads<Rest...>
{
using overloads<Rest...>::call;
static identity<U> call(U);
};
template <typename U>
struct overloads<U>
{
static identity<U> call(U);
};
template <typename... E_>
static identity<eval<decltype(overloads<E_...>::call(std::declval<T>()))>>
best_conv(int);
template <typename...>
static identity<void> best_conv(...);
public:
using type = eval<decltype(best_conv<E...>(0))>;
};
template <typename... T>
using best_conversion_t = eval<best_conversion<T...>>;
Demo. For the "unspecified" case above this template will give you int.
The basic idea is to put a bunch of overloaded functions with one parameter into different scopes that name lookup will look in, with the parameter and return type of each overload corresponding to one of the types in our list.
overloads does this job by recursively introducing one declaration of call at a time and adapting all previously introduced calls from the base specializations via using declarations. That way all calls are in different scopes but are considered equally when it comes to overload resolution.
Then apply SFINAE in a function template best_conv to check whether the call to call (inside overloads) is well-formed: If it is, take the return type (which is by definition the parameter type) of the selected declaration and use it as our result - it will be the type we are looking for. Also provide a second overload of best_conv that returns void and can be selected as a default (when SFINAE applies in the first overload and kicks it out of the candidate set).
The return types use identity<> to avoid type decays when working with e.g. array or function pointer types.

How to turn method pointer types into function pointer types

Suppose you have template method that takes a pointer to a method of any type. Inside that template, is there a way to turn the template argument, which is a method pointer type (void(A::*)() for example), into a function pointer type with the same return type and parameters, but without the class information (void(*)())?
I looked at the <type_traits> header but didn't find anything that would help.
A simple way in C++11 is partial specialization with a variadic template:
template<class PMF> struct pmf_to_pf;
template<class R, class C, class... Args>
struct pmf_to_pf<R (C::*)(Args...)>{
using type = R(*)(Args...); // I like using aliases
};
Note that you need a seperate partial specialization for const member-functions (and theoretically for ref-qualified ones aswell):
template<class R, class C, class... Args>
struct pmf_to_pf<R (C::*)(Args...) const>{
using type = R(*)(Args...);
};
With cv-qualifiers ({nil,const}, {nil,volatile}) and ref-qualifiers ({nil, &, &&}), you have a total of 2*2*3 == 12 partial specializations to provide†. You normally can leave out the volatile qualification ones, dropping the total down to "just" 6. Since most compilers don't support function-ref-qualifiers, you only need 2 really, but be aware of the rest.
For C++03 (aka compilers without variadic templates), you can use the following non-variadic form:
template<class PMF> struct pmf_to_pf;
template<class Sig, class C>
struct pmf_to_pf<Sig C::*>{
typedef Sig* type; // 'Sig' is 'R(Args...)' and 'Sig*' is 'R(*)(Args...)'
};
Although this doesn't quite work with cv-qualifiers, since I think C++03 didn't allow cv-qualified signatures (e.g. R(Args...) const) on which you could partially specialize to remove the const.
† The {...} denote a set of a certain qualifier, with nil representing the empty set. Examples:
void f() const& would be {const}, {nil}, {&}
void f() && would be {nil}, {nil}, {&&}
And so on.