Cannot infer template argument 'T' when a second parameter includes 'T' - c++

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.

Related

Expansion of variadic type parameters of outer struct within member struct's template-template parameter

If you have a template struct then you can use T as the type of a value parameter of a member template:
template <typename T>
struct SMoo
{
template <T I, typename T2>
struct SBaa {};
};
SMoo<int>::SBaa<3, bool> moobaa;
You can do the same with a variadic outer template:
template <typename ... VT>
struct SMoo
{
template <VT ... VI, typename T2>
struct SBaa {};
};
SMoo<int, bool>::SBaa<3, true, bool> moobaa;
You can also use T as the type of a value parameter of a template-template parameter of a member template:
template <typename T>
struct SMoo
{
template <template <T I, typename T2> typename TT>
struct SBaa {};
};
template <int I, typename T>
struct SCaw {};
SMoo<int>::SBaa<SCaw> moobaacaw;
You can combine these to get:
template <typename ... VT>
struct SMoo
{
template <template <VT ... VI, typename T2> typename TT>
struct SBaa {};
};
template <int I, typename T>
struct SCaw {};
SMoo<int>::SBaa<SCaw> moobaacaw;
But this seems to fail as soon as there's more than one VT parameter:
template <typename ... VT>
struct SMoo
{
template <template <VT ... VI, typename T2> typename TT>
struct SBaa {};
};
template <int I, typename VT>
struct SCaw {};
template <int I, bool B, typename VT>
struct SMew {};
SMoo<int>::SBaa<SCaw> moobaacaw;
//SMoo<int, bool>::SBaa<SMew> moobaamew; // <<=== FAILS HERE (WHY?)
SMoo<int, bool>::SBaa<SCaw> moobaacaw2; // <<=== ALSO, DOESN'T FAIL HERE (WHY?)
the error is:
<FILE>:5:27: error: type/value mismatch at argument 1 in template parameter list for ‘template<class ... VT> template<template<template<VT ...VI, class T2> class TT> template<class ... VT> template<VT ...VI, class T2> class TT> struct SMoo<VT>::SBaa’
15 | SMoo<int, bool>::SBaa<SMew> moobaamew; // <<=== FAILS HERE (WHY?)
| ^
<FILE>:15:27: note: expected a template of type ‘template<class ... VT> template<VT ...VI, class T2> class TT’, got ‘template<int I, bool B, class VT> struct SMew’
It's like the VT parameter isn't being matched accurately.
Can this be fixed?

Is there a way to pass all type template parameters in an old class template into a new class template?

I've created a simple template class called tuple_tag which is identical to std::tuple but only acts as a tag.
// tuple_tag
template <typename...> struct tuple_tag {};
// tuple_tag_element
template <size_t I, typename T>
struct tuple_tag_element;
template <size_t I, typename Head, typename... Tail>
struct tuple_tag_element<I, tuple_tag<Head, Tail...>>
: tuple_tag_element<I - 1, tuple_tag<Tail...>> {};
template <typename Head, typename... Tail>
struct tuple_tag_element<0, tuple_tag<Head, Tail...>>
: std::type_identity<Head> {};
// tuple_tag_element_t
template <size_t I, typename T>
using tuple_tag_element_t = tuple_tag_element<I, T>::type;
// tuple_tag_size
template <typename T>
struct tuple_tag_size;
template <typename T> requires (std::is_reference_v<T> || std::is_const_v<T>)
struct tuple_tag_size<T> : tuple_tag_size<std::remove_cvref_t<T>> {};
template <typename... Ts>
struct tuple_tag_size<tuple_tag<Ts...>>
: std::integral_constant<size_t, sizeof...(Ts)> {};
// tuple_tag_size_v
template <typename T>
inline constexpr size_t tuple_tag_size_v = tuple_tag_size<T>::value;
Here:
using new_type_1 = to_tuple_type<tuple_tag<int, double>>::type;
// new_type_1 = std::tuple<int, double>;
using new_type_2 = to_tuple_tag_type<std::tuple<int, double>>::type;
// new_type_2 = tuple_tag<int, double>;
Where to_tuple_type takes a type template parameter tuple_tag<...> which will be converted into type std::tuple<...>, and to_tuple_tag_type takes a type template parameter std::tuple<...> which will be converted into type tuple_tag<...>.
What I am trying to achieve here is to pass all type template parameters from tuple_tag into std::tuple and vice-versa.
This is my prototype for to_tuple_type where it fails:
template <typename TupleTag>
struct to_tuple_type {
using type = std::tuple<...>;
};
Where type alias will be expanded into:
using type = std::tuple<tuple_tag_element_t<Index, TupleTag>...>;
...
using type = std::tuple<
tuple_tag_element_t<0, TupleTag>,
tuple_tag_element_t<1, TupleTag>,
...,
tuple_tag_element_t<N - 1, TupleTag>
>;
Where N is equal to tuple_tag_size_v<TupleTag>.
What I could only think of is to use std::index_sequence but I don't know where do I introduce the pack.
3 steps. First, make a pack
using indexes=std::make_index_sequence<tuple_tag_size<TupleTag>;
then have a helper that expands the pack. I like this one:
template<auto x>
using constant_t=std::integral_constant<decltype(x),x>;
template<auto x>
constexpr constant_t<x> constant={};
template<std::size_t...Is>
constexpr auto all_indexes( std::index_sequence<Is...> ){
return [](auto f){
return f(constant<Is>...);
};
}
now we can
template<class T>
struct tag_t{using type=T;};
template<class T>
constexpr tag_t<T> tag={};
using type=typename decltype(all_indexes(indexes{})([](auto...Is){
return tag<std::tuple<tuple_tag_element_t<Is, TupleTag>...>;
}))::type;
if that has no tpyos.
There is a simple solution that applies partial template specialization:
// to_tuple_type
template <typename Tup>
struct to_tuple_type;
template <typename... Ts>
struct to_tuple_type<tuple_tag<Ts...>> : std::type_identity<std::tuple<Ts...>> {};
// to_tuple_type_t
template <typename Tup>
using to_tuple_type_t = to_tuple_type<Tup>::type;
// to_tuple_tag_type
template <typename Tup>
struct to_tuple_tag_type;
template <typename... Ts>
struct to_tuple_tag_type<std::tuple<Ts...>> : std::type_identity<tuple_tag<Ts...>> {};
// to_tuple_tag_type_t
template <typename Tup>
using to_tuple_tag_type_t = to_tuple_tag_type<Tup>::type;

Is this template syntax illegal?

I'm getting an "internal compiler error" with this using GCC 4.9.2:
#include <type_traits>
template <typename T, typename, int, template <typename U, U, U> class>
struct Sort;
template <typename T, template <T...> class Z, T N, T... Is,
template <typename U, U, U> class Comparator>
struct Sort<T, Z<N, Is...>, 0, Comparator> {
template <T I>
struct less_than : std::integral_constant<bool, Comparator<T, I, N>::value> {
};
};
int main() {}
The error message states:
c:\ADandD>g++ -std=c++14 ComparatorAndSorterTGeneralized.cpp
ComparatorAndSorterTGeneralized.cpp:254:80: internal compiler error: in tsubst,
at cp/pt.c:11738
template<T I>
struct less_than : std::integral_constant<bool, Comparator<T,I,N>::value> {};
^
Please submit a full bug report,
with preprocessed source if appropriate.
See http://gcc.gnu.org/bugs.html for instructions.
The issue is the template <typename U, U, U> class Comparator being used. I've never tried this before. At first I tried the template <typename T, T, T> class Comparator, but that would not compile because of the template shadowing, so I knew that was illegal. And then changing it to U still did not compile, so I thought the whole idea is not allowed.
Update: Upon instantiating, this compiles in Visual Studio 2015 Preview:
#include <type_traits>
template <typename T, typename, int, template <typename U, U, U> class>
struct Sort;
template <typename T, template <T...> class Z, T N, T... Is,
template <typename U, U, U> class Comparator>
struct Sort<T, Z<N, Is...>, 0, Comparator> {
template <T I>
struct less_than : std::integral_constant<bool, Comparator<T, I, N>::value> {
};
};
template <int...>
struct index_sequence {};
template <typename T, T A, T B>
struct LessThan : std::integral_constant < bool,
A<B> {};
enum { QuickSort, MergeSort, InsertionSort };
int main() {
Sort<int, index_sequence<4, 5, 6, 1, 2, 7>, QuickSort, LessThan> quickSort;
}
template <typename T, typename, int, template <typename U, U, U> class>
struct Sort;
This is perfectly legal.
It could be redeclared like this, giving names to all the parameters:
template <typename T, typename T2, int I, template <typename U, U X, U Y> class TT>
struct Sort;
It declares a class template Sort which has four template parameters, the type parameter T, a second type parameter T2 (unnamed in the original), a non-type template parameter I, and a template template parameter TT.
The template template parameter TT must a class template taking three template parameters, U is a type parameter and the second and third (X and Y) are non-type template parameters of type U.
A suitable argument for the fourth template parameter of Sort might be something like:
template <typename T, T t1, T t2>
class Foo
{ static const bool value = t1 < t2; };
which would be instantiated like:
Foo<int, 1, 2> fi;
or
Foo<char, 'a', 'b'> fc;

Check if a class is equal to a template instance or a normal class

I have an integral_constant to determine if a class is in a provided list of classes:
template <typename T> using decay_t = typename std::decay<T>::type;
template <typename... Args> struct SOneOf : std::integral_constant<bool, false>
{
};
template <typename T1, typename T2, typename... Tail> struct SOneOf<T1, T2, Tail...>
: std::integral_constant<bool, SOneOf<T1, T2>::value || SOneOf<T1, Tail...>::value>
{
};
template <typename T1, typename T2> struct SOneOf<T1, T2>
: std::integral_constant<bool, std::is_same<decay_t<T1>, decay_t<T2>>::value>
{
};
template <typename T> struct SOneOf<T> : std::integral_constant<bool, false>
{
};
I also know that I can give templated classes as template arguments via template <template <typename> class T>.
Assuming I have two classes
template <typename T> class CClassA;
template <typename T> class CClassB;
and am in a function template <typename TF> function f().
How can I generically check if TF (e.g. int or CClassA<double>) is a CClassA or a CClassB or a float?
I'd like to achieve something like
SOneOf<TF, CClassA, CClassB, float>::value
If you're trying to get that information inside the function template you can use partial specialization:
template <bool...> struct bool_pack;
template <bool... B>
using any_true = std::integral_constant<bool,
!std::is_same<bool_pack<false, B...>, bool_pack<B..., false>>{}>;
namespace detail {
template <template <class...> class, typename>
struct IsSpec : std::false_type {};
template <template <class...> class T, typename... U>
struct IsSpec<T, T<U...>> : std::true_type {};
}
template <typename U, template <class...> class... T>
using IsSpecialization = any_true<detail::IsSpec<T, std::decay_t<U>>{}...>;
Usage would be simple:
static_assert( IsSpecialization<CClassB<int>, CClassA, CClassB>{}, "" );
static_assert( !IsSpecialization<void, CClassA, CClassB>{}, "" );
Demo. The extra comparison for float has to be done separately, e.g. via std:is_same.
If you need different code based on the template the type is a specialization of, use overloading.
template <typename Arg>
void f( CClassA<Arg> const& obj ) { /* … */ }
template <typename Arg>
void f( CClassB<Arg> const& obj ) { /* … */ }

Do static_assert on every other type in a template

How can I do static_asserts (or other checks) on every other type in a template?
template<typename... Ts> //T1,T2,T3,...
struct foo {
//How can I
//for T1,T3,T5,T7,...
//do some checks, for example:
//static_assert(std::is_default_constructible<Tn>::value,"invalid type");
//static_assert(std::is_copy_constructible<Tn>::value,"invalid type");
};
Please try this:
#include <type_traits>
template <typename... Ts>
struct default_constructible;
template <typename T>
struct default_constructible<T>
{
static constexpr bool value = std::is_default_constructible<T>::value;
};
template <>
struct default_constructible<>
{
static constexpr bool value = true;
};
template <typename T, typename U, typename... Ts>
struct default_constructible<T, U, Ts...>
{
static constexpr bool value = std::is_default_constructible<T>::value && default_constructible<Ts...>::value;
};
template <typename... Ts>
struct foo
{
static_assert(default_constructible<Ts...>::value, "");
};
class A { A() = delete; };
template class foo<int, bool>; // Compiles
template class foo<int, bool, A>; // Does not compile
Demo
You could try this:
template <template <typename...> class Pred, typename ...Args> struct check_odd;
template <template <typename...> class Pred>
struct check_odd<Pred> : std::true_type { };
template <template <typename...> class Pred, typename T>
struct check_odd<Pred, T> : Pred<T> { };
template <template <typename...> class Pred, typename T1, typename T2, typename ...Args>
struct check_odd<Pred, T1, T2, Args...>
{
static constexpr bool value = Pred<T1>::value && check_odd<Pred, Args...>::value;
};
Usage:
static_assert(check_odds<std::is_arithmetic, T1, T2, T3, T4, T5, T6>::value,
"Not all odd types are arithmetic.");
Obvious routes of generalization are parametrizing the combiner (currently hardcoded as "AND", or &&), and giving the predicate additional parameters.