C++ Nesting SFINAE Template Produces Compilation Error - c++

I'm trying to create an addition operator for a custom template class, where the first argument is allowed to be either an instance of my class or a basic numeric type. My operator has a definition similar to the example code below:
#include <type_traits>
template<typename T>
struct MyTemplateStruct {
T val;
};
template<typename T, typename U>
struct MyCommonType {
typedef std::common_type_t<T, U> type;
};
template<typename T, typename U>
using MyCommonTypeT = typename MyCommonType<T, U>::type;
template<typename T, typename U>
MyTemplateStruct<MyCommonTypeT<T, U>> operator +(
MyTemplateStruct<T> const& a, MyTemplateStruct<U> const& b)
{
return { a.val + b.val };
}
template<typename T, typename U>
MyTemplateStruct<MyCommonTypeT<T, U>> operator +(
T const a, MyTemplateStruct<U> const& b)
{
return { a + b.val };
}
int main()
{
MyTemplateStruct<double> a{ 0 }, b{ 0 };
a = a + b;
return 0;
}
My expectation was that due to SFINAE, the compilation error that results from trying to instantiate the second operator definition with T = MyTemplateStruct<double>, U = double would just exclude that template from the list of potential matches. However, when I compile, I'm getting the following error:
/usr/include/c++/7/type_traits: In substitution of ‘template<class ... _Tp> using common_type_t = typename std::common_type::type [with _Tp = {MyTemplateStruct, double}]’:
main.cpp:18:38: required from ‘struct MyCommonType<MyTemplateStruct, double>’
main.cpp:31:39: required by substitution of ‘template<class T, class U> MyTemplateStruct<typename MyCommonType<T, U>::type> operator+(T, const MyTemplateStruct&) [with T = MyTemplateStruct; U = double]’
main.cpp:40:13: required from here
/usr/include/c++/7/type_traits:2484:61: error: no type named ‘type’ in ‘struct std::common_type, double>’
If I directly utilize std::common_type_t in the operator definition, instead of using my wrapper template MyCommonTypeT, then SFINAE works as I expect and there is no error. How can I make the above code compile when I have to wrap the call to std::common_type in another template?

You need to make MyCommonTypeT<T, U> (i.e. MyCommonType<T, U>::type) itself invalid, it's not enough that std::common_type_t<T, U> is invalid when declaring type in MyCommonType. For example you can specialize MyCommonType, when MyTemplateStruct is specified as template argument, type is not declared.
template<typename T, typename U>
struct MyCommonType {
typedef std::common_type_t<T, U> type;
};
template<typename T, typename U>
struct MyCommonType<MyTemplateStruct<T>, U> {};
template<typename T, typename U>
struct MyCommonType<T, MyTemplateStruct<U>> {};
LIVE

Related

Cannot infer template argument 'T' when a second parameter includes 'T'

Given this template function:
template <
typename T,
typename U,
typename = std::enable_if<
std::is_same_v<U, std::unique_ptr<T>> ||
std::is_same_v<U, std::shared_ptr<T>>>>
T foo(U val) {
if constexpr (std::is_same_v<U, std::unique_ptr<T>>) {
return *val;
}
return *val;
}
I want to call it like this:
int x = foo(std::make_unique<int>(1));
But, it's giving this error:
Candidate template ignored: couldn't infer template argument 'T'
I can fix it by providing the type T explicitly:
int x = foo<int>(std::make_unique<int>(1));
Is it possible to fix it such that it can infer? The type is clearly embedded in the typename U but it does not seem to "propagate" to T.
SFINAE constraints don't affect template argument deduction. Your compiler has to deduce T and U first, without even looking at your enable_if_t<...>.
I'd do something like this:
#include <memory>
#include <type_traits>
namespace impl
{
template <typename A, template <typename...> typename B>
struct specialization_of : std::false_type {};
template <template <typename...> typename T, typename ...P>
struct specialization_of<T<P...>, T> : std::true_type {};
}
template <typename A, template <typename...> typename B>
concept specialization_of = impl::specialization_of<A, B>::value;
template <typename T>
requires specialization_of<T, std::unique_ptr> || specialization_of<T, std::shared_ptr>
auto foo(T val)
{
if constexpr (specialization_of<T, std::unique_ptr>)
{
return *val;
}
return *val;
}
[I also want] std::is_same_v<U, std::function<T()>>
Then you need a specialized template just for this task. Something like:
template <typename T, typename U>
struct foo : std::false_type {};
template <typename T, typename U>
struct foo<std::function<T()>, U> : std::is_same<T, U> {};
Then foo<std::function<int()>, int> is true.

How to require an exact function signature in the detection idiom?

Let's suppose I have a type T and I want to detect whether it has a subscript operator which I can call with with another type Index. The following example works just fine:
#include <type_traits>
#include <vector>
template < typename T, typename Index >
using subscript_t = decltype(std::declval<T>()[std::declval<Index>()]);
int main()
{
using a = subscript_t< std::vector<int>, size_t >;
using b = subscript_t< std::vector<int>, int >;
}
However, I want the function to be detected if and only if the function signature matches exactly. In the example above I would like the statement subscript_t< std::vector<int>, int >; to throw an error like no viable overloaded operator[], because the signature of the subscript operator for std::vector is
std::vector<T, std::allocator<T>>::operator[](size_type pos);
where size_type in GCC is unsigned long. How can I avoid the implicit conversion from int to size_t to take place?
With is_detected, you may do:
template <typename T, typename Ret, typename Index>
using subscript_t = std::integral_constant<Ret (T::*) (Index), & T::operator[]>;
template <typename T, typename Ret, typename Index>
using has_subscript = is_detected<subscript_t, T, Ret, Index>;
static_assert(has_subscript<std::vector<int>, int&, std::size_t>::value, "!");
static_assert(!has_subscript<std::vector<int>, int&, int>::value, "!");
Demo
I wrote this in SO Documentation:
is_detected
To generalize type_trait creation:based on SFINAE
there are experimental traits detected_or, detected_t, is_detected.
With template parameters typename Default, template <typename...> Op and typename ... Args:
is_detected: alias of std::true_type or std::false_type depending of the validity of Op<Args...>
detected_t: alias of Op<Args...> or nonesuch depending of validity of Op<Args...>.
detected_or: alias of a struct with value_t which is is_detected, and type which is Op<Args...> or Default depending of validity of Op<Args...>
which can be implemented using std::void_t for SFINAE as following:
namespace detail {
template <class Default, class AlwaysVoid,
template<class...> class Op, class... Args>
struct detector
{
using value_t = std::false_type;
using type = Default;
};
template <class Default, template<class...> class Op, class... Args>
struct detector<Default, std::void_t<Op<Args...>>, Op, Args...>
{
using value_t = std::true_type;
using type = Op<Args...>;
};
} // namespace detail
// special type to indicate detection failure
struct nonesuch {
nonesuch() = delete;
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
void operator=(nonesuch const&) = delete;
};
template <template<class...> class Op, class... Args>
using is_detected =
typename detail::detector<nonesuch, void, Op, Args...>::value_t;
template <template<class...> class Op, class... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;
template <class Default, template<class...> class Op, class... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;
Traits to detect presence of method can then be simply implemented:
template <typename T, typename ...Ts>
using foo_type = decltype(std::declval<T>().foo(std::declval<Ts>()...));
struct C1 {};
struct C2 {
int foo(char) const;
};
template <typename T>
using has_foo_char = is_detected<foo_type, T, char>;
static_assert(!has_foo_char<C1>::value, "Unexpected");
static_assert(has_foo_char<C2>::value, "Unexpected");
static_assert(std::is_same<int, detected_t<foo_type, C2, char>>::value,
"Unexpected");
static_assert(std::is_same<void, // Default
detected_or<void, foo_type, C1, char>>::value,
"Unexpected");
static_assert(std::is_same<int, detected_or<void, foo_type, C2, char>>::value,
"Unexpected");
You can do this:
template <typename Index, typename ClassType, typename ReturnType>
constexpr bool arg_t(ReturnType (ClassType::*)(Index))
{
return true;
}
template <typename T, typename Index>
struct subscript_t
{
static_assert(arg_t<Index>(&T::operator[]));
};
Note that you'll need to instantiate subscript_t, not merely name its type:
int main()
{
subscript_t< std::vector<int>, size_t > a;
subscript_t< std::vector<int>, int > b;
}
Another way, which lets you determine the exact error message:
template <typename ClassType, typename ReturnType, typename ArgType>
constexpr ArgType arg_t(ReturnType (ClassType::*)(ArgType))
{
return {};
}
template <typename T, typename Index>
struct subscript_t
{
using Actual = decltype(arg_t(&T::operator[]));
static_assert(std::is_same<Index, Actual>::value, "oops");
};

c++ template substitution error

I really wanted to solve this myself, but spending hours looking at the same code again and again is not making me happy. The error message looks quite straightforward, and it points me to the Not2 template. It complains that there is no Apply member, but it clearly has Apply, two of them. Function2 and Not2 has a very similar form, but the compiler doesn't say anything about Function2, so something's going wrong inside Not2. Not2 is meant to take a Function2 template and negate the result.
I removed irrelevant code as much as possible, but still the code seems quite long. Just skip away if you think reading all that isn't worth your time.
main.cpp:130:24: error: type/value mismatch at argument 1 in template parameter list for 'template<class T> constexpr const int unbox<T>'
using Apply = Box<!unbox<T::template Apply<U>::template Apply<V>>>;
^
main.cpp:130:24: note: expected a type, got 'typename T::Apply<U>::Apply<V>'
main.cpp:130:70: error: template argument 1 is invalid
using Apply = Box<!unbox<T::template Apply<U>::template Apply<V>>>;
^
main.cpp: In instantiation of 'struct Sort_<List<Box<2>, Box<1> > >':
main.cpp:155:37: required by substitution of 'template<class T> using Sort = typename Sort_::Type [with T = List<Box<2>, Box<1> >]'
main.cpp:159:38: required from here
main.cpp:151:77: error: no class template named 'Apply' in 'Not2<Function2<LessThan_> >::Apply<Box<2> > {aka struct Not2<Function2<LessThan_> >::Apply_<Box<2> >}'
Filter<Tail<T>, Not2<LessThan>::template Apply<Head<T>>::template Apply>> Type;
^
template<int n>
struct Box
{
};
template<typename T>
struct Unbox_;
template<int n>
struct Unbox_<Box<n>>
{
static constexpr int value = n;
};
template<typename T>
constexpr int unbox = Unbox_<T>::value;
template<typename ...>
struct List
{
};
template<>
struct List<>
{
};
template<typename, typename>
struct Cons_;
template<typename T, typename ...Ts>
struct Cons_<T, List<Ts...>>
{
typedef List<T, Ts...> Type;
};
template<typename T, typename U>
using Cons = typename Cons_<T, U>::Type;
template<typename>
struct Head_;
template<typename T, typename ...Ts>
struct Head_<List<T, Ts...>>
{
typedef T Type;
};
template<typename T>
using Head = typename Head_<T>::Type;
template<typename>
struct Tail_;
template<typename T, typename ...Ts>
struct Tail_<List<T, Ts...>>
{
typedef List<Ts...> Type;
};
template<typename T>
using Tail = typename Tail_<T>::Type;
template<typename T, typename U>
struct Merge_
{
typedef Cons<Head<T>, typename Merge_<Tail<T>, U>::Type> Type;
};
template<typename T>
struct Merge_<List<>, T>
{
typedef T Type;
};
template<typename T, typename U>
using Merge = typename Merge_<T, U>::Type;
template<typename, typename T, typename>
struct If_
{
typedef T Type;
};
template<typename T, typename U>
struct If_<Box<0>, T, U>
{
typedef U Type;
};
template<typename T, typename U, typename V>
using If = typename If_<T, U, V>::Type;
template<typename T, template<typename> class U>
struct Filter_
{
typedef If<U<Head<T>>, Cons<Head<T>, typename Filter_<Tail<T>, U>::Type>,
typename Filter_<Tail<T>, U>::Type> Type;
};
template<template<typename> class T>
struct Filter_<List<>, T>
{
typedef List<> Type;
};
template<typename T, template<typename> class U>
using Filter = typename Filter_<T, U>::Type;
template<template<typename, typename> class T>
struct Function2
{
template<typename U>
struct Apply_
{
template<typename V>
using Apply = T<U, V>;
};
template<typename U>
using Apply = Apply_<U>;
};
template<typename T>
struct Not2
{
template<typename U>
struct Apply_
{
template<typename V>
using Apply = Box<!unbox<T::template Apply<U>::template Apply<V>>>;
};
template<typename U>
using Apply = Apply_<U>;
};
template<typename T, typename U>
struct LessThan_2
{
typedef Box<unbox<T> < unbox<U>> Type;
};
template<typename T, typename U>
using LessThan_ = typename LessThan_2<T, U>::Type;
using LessThan = Function2<LessThan_>;
template<typename T>
struct Sort_
{
typedef Merge<Merge<Filter<Tail<T>, LessThan::Apply<Head<T>>::template Apply>, List<Head<T>>>,
Filter<Tail<T>, Not2<LessThan>::template Apply<Head<T>>::template Apply>> Type;
};
template<typename T>
using Sort = typename Sort_<T>::Type;
int main()
{
typedef Sort<List<Box<2>, Box<1>>> L;
}
I believe you are simply missing a typename inside the unbox template argument list on the line (130) giving you the error.
Also, you do not seem to be doing any sorting in your Sort_ and Merge_ templates, since they simply concatenate lists using the head as pivot Coliru illustration. I guess your code is not completed yet.

Conditional Macros or expanding templates

I have a templated struct like this:
class Context {
template<typename T>
T construct();
};
template<typename T, typename ...Args>
struct aggregate {
T construct(Context& ctx) {
return { std::move(ctx.construct<Args>())... };
}
};
The problem with this is easy to see: When a user requests uses it like this:
typedef struct {
float xyz[3];
} Vector3;
using ConstructVector = aggregate<Vector3, float[3]>;
Here an error is generated because this would call the templated function Context.construct with T = float[3] resulting in a function returning an array. What I thus want is some way to expand { std::move(ctx.construct<Args>())... } to this: {{ std::move(ctx.construct<float>()), std::move(ctx.construct<float>()), std::move(ctx.construct<float>()) }} or more general, any array of type T (with constexpr size N) should be extended to a construct repeating std::move(ctx.construct<T>()) exactly N times and should be wrapped in an additional pair of {}.
Is this a wrong approach? Is there another way to initialize an array of values in an aggregate statement?
I get the following error from g++ (4.9.1):
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/utilitysupplier.hpp: In instantiation of 'typename io::Supplier<T>::item_t io::utility::aggregate_supplier<T, args>::supply(io::Context&) const [with T = Vector3; args = float [3]; typename io::Supplier<T>::item_t = Vector3]':
../test/main.cpp:185:1: required from here
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/utilitysupplier.hpp:42:37: error: no matching function for call to 'io::Context::construct()'
return { ctx.construct<args>()... };
^
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/utilitysupplier.hpp:42:37: note: candidate is:
In file included from C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.hpp:141:0,
from C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Reader.hpp:13,
from C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Environment.hpp:12,
from ../test/main.cpp:14:
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.tpp:37:28: note: template<class T> typename io::supply_t<T>::type io::Context::construct()
typename supply_t<T>::type Context::construct() {
^
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.tpp:37:28: note: template argument deduction/substitution failed:
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.tpp: In substitution of 'template<class T> typename io::supply_t<T>::type io::Context::construct() [with T = float [3]]':
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/utilitysupplier.hpp:42:37: required from 'typename io::Supplier<T>::item_t io::utility::aggregate_supplier<T, args>::supply(io::Context&) const [with T = Vector3; args = float [3]; typename io::Supplier<T>::item_t = Vector3]'
../test/main.cpp:185:1: required from here
C:\Users\Carbon\Documents\CreativeWorkspace\EpicRPG\SmartIO/smartio/Context.tpp:37:28: error: function returning an array
Generating a nested braced-init list is impossible, but generating a flatten one is possible and is suitable for aggregate.
The approach shown below uses C++14 though, with the help of std::make_index_sequence, but you can implement such a thing in C++11 as well:
template<class... Ts>
struct list;
template<class A, class B>
struct merge;
template<class... As, class... Bs>
struct merge<list<As...>, list<Bs...>>
{
using type = list<As..., Bs...>;
};
template<std::size_t N, class T>
using just = T;
template<class T, class Index>
struct repeat_impl;
template<class T, std::size_t... Ns>
struct repeat_impl<T, std::index_sequence<Ns...>>
{
using type = list<just<Ns, T>...>;
};
template<class T, int N>
using repeat = typename repeat_impl<T, std::make_index_sequence<N>>::type;
template<class T>
struct to_list
{
using type = list<T>;
};
template<class T, int N>
struct to_list<T[N]>
{
using type = repeat<T, N>;
};
template<class... Ts>
struct flatten;
template<>
struct flatten<>
{
using type = list<>;
};
template<class T, class... Ts>
struct flatten<T, Ts...>
{
using type = typename merge<typename to_list<T>::type, typename flatten<Ts...>::type>::type;
};
flatten<float[3], int[2]>::type will return you list<float, float, float, int, int, int>.
Now we can implement aggregate as below:
struct Context
{
template<typename T>
T construct();
};
template<class T, class List>
struct aggregate_impl;
template<class T, class... Args>
struct aggregate_impl<T, list<Args...>>
{
static T construct(Context& ctx)
{
return {ctx.construct<Args>()...};
}
};
template<class T, class... Args>
using aggregate = aggregate_impl<T, typename flatten<Args...>::type>;
Now you can do:
using ConstructVector = aggregate<Vector3, float[3]>;
Context ctx;
ConstructVector::construct(ctx);
LIVE DEMO

SFINAE enable_if explicit constructor

I'm trying to switch between an explicit and an implicit conversion constructor via enable_if.
My code currently looks like
#include <type_traits>
#include <cstdint>
enum class enabled {};
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, typename T = void> using disable_if_t = typename std::enable_if<!B, T>::type;
template <std::intmax_t A> struct SStruct
{
static constexpr std::intmax_t a = A;
};
template <typename T> struct SCheckEnable : std::integral_constant<bool, T::a == 0>
{
};
template <typename U, typename T> class CClass
{
public:
template <typename T2, enable_if_t<SCheckEnable<U>::value, enabled>...> constexpr CClass(T2 v) : val(v) {};
template <typename T2, disable_if_t<SCheckEnable<U>::value, enabled>...> explicit constexpr CClass(T2 v) : val(v) {};
private:
T val;
};
int main()
{
CClass<SStruct<0>, double> a = 1; // should use implicit constructor
CClass<SStruct<1>, double> b = CClass<SStruct<1>, double>(1); // should use explicit constructor
}
The true in the enable_ifs is dependent of the template parameter U
If I try to compile this minimal example with g++ 4.9.1 and --std=c++11 enabled I get the following errors
sfinae.cpp: In substitution of ‘template<bool B, class T> using disable_if_t = typename std::enable_if<(! B), T>::type [with bool B = true; T = enabled]’:
sfinae.cpp:13:52: required from here
sfinae.cpp:7:95: error: no type named ‘type’ in ‘struct std::enable_if<false, enabled>’
template <bool B, typename T = void> using disable_if_t = typename std::enable_if<!B, T>::type;
^
sfinae.cpp:19:68: error: prototype for ‘constexpr CClass<U, T>::CClass(T2)’ does not match any in class ‘CClass<U, T>’
template <typename U, typename T> template <typename T2> constexpr CClass<U, T>::CClass(T2 v) : val(v)
^
sfinae.cpp:13:77: error: candidates are: template<class U, class T> template<class T2, int ...<anonymous> > constexpr CClass<U, T>::CClass(T2)
template <typename T2, disable_if_t<true, enabled>...> explicit constexpr CClass(T2 v);
^
sfinae.cpp:12:67: error: template<class U, class T> template<class T2, enabled ...<anonymous> > constexpr CClass<U, T>::CClass(T2)
template <typename T2, enable_if_t<true, enabled>...> constexpr CClass(T2 v);
^
Any idea how to select between explicit and implicit construction based on parameter U here?
Use
template <class...> struct null_v : std::integral_constant<int, 0> {};
and define the constructors as
template <typename T2,
long = null_v<enable_if_t<SCheckEnable<U>::value, T2>>::value>
constexpr CClass(T2 v) : val(v) {};
template <typename T2,
int = null_v<disable_if_t<SCheckEnable<U>::value, T2>>::value>
explicit constexpr CClass(T2 v) : val(v) {};
Making the argument dependent and actually instantiated.
Demo.
[temp.deduct]/8:
If a substitution results in an invalid type or expression, type
deduction fails. An invalid type or expression is one that would be
ill-formed if written using the substituted arguments.
In your case the error occurs outside of any substitution, so that's not causing a deduction failure but rather makes your code ill-formed.