I want to determine the underlying template of a template parameter by using a combination of a template alias and template specializations. The follwing code compiles fine on gcc 4.8, 6.2.1 but not on clang 3.5, 3.8.
#include <iostream>
template <typename T> struct First {};
template <typename T> struct Second {};
template <template <typename> class F, typename T> struct Foo {};
template <typename T> struct Foo<First, T>
{
void f() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
template <typename T> struct Foo<Second, T>
{
void f() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
template <typename F, typename T> struct Resolution {};
template <typename T> struct Resolution<First<T>, T>
{
template <typename P> using type = First<P>;
};
template <typename T> struct Resolution<Second<T>, T>
{
template <typename P> using type = Second<P>;
};
int main()
{
Foo<Resolution<First<int>, int>::type, float> my_foo;
my_foo.f(); // main.cpp:34:12: error: no member named 'f' in 'Foo<Resolution<First<int>, int>::type, float>'
return 0;
}
Which behavior is standard conformant?
Answer: This is a know bug in the C++ Standard Core Language , as described by T.C. in the comments . http://wg21.link/cwg1286
Related
I want to create a class specialization that has the same implementation if it gets passed any std::variant or any boost::variant. I tried to play around with std::enable_if, std::disjunction and std::is_same but I couldn't make it compile. Here is a code sample to show what I want to achieve.
#include <variant>
#include <iostream>
#include <boost/variant.hpp>
template <typename T>
struct TypeChecker;
template <typename T>
struct TypeChecker<T>
{
void operator()()
{
std::cout << "I am other type\n";
}
}
template <typename ... Ts> // I want to be able to capture Ts... in TypeChecker scope
struct TypeChecker<std::variant<Ts...> or boost::variant<Ts...>> // what to insert here?
{
void operator()()
{
std::cout << "I am either std::variant or boost::variant\n";
}
}
int main()
{
TypeChecker<std::variant<int, float>>{}();
TypeChecker<boost::variant<int, float>>{}();
TypeChecker<int>{}();
}
Expected result:
I am either std::variant or boost::variant
I am either std::variant or boost::variant
I am other type
You can use a template template parameter e.g. like this
template <typename T>
struct TypeChecker {
void operator()() {
std::cout << "I am other type\n";
}
};
template<typename ... Ts, template<typename...> typename V>
requires std::same_as<V<Ts...>, std::variant<Ts...>> ||
std::same_as<V<Ts...>, boost::variant<Ts...>>
struct TypeChecker<V<Ts...>>
{
void operator()()
{
std::cout << "I am either std::variant or boost::variant\n";
}
};
Note that this uses C++20 constraints. If you can't use C++20 you can use std::enable_if instead like e.g. this:
template<typename ... Ts, template<typename...> typename V>
struct TypeChecker<V<Ts...>> : std::enable_if_t<
std::is_same_v<V<Ts...>, std::variant<Ts...>> ||
std::is_same_v<V<Ts...>, boost::variant<Ts...>>, std::true_type>
{
void operator()()
{
std::cout << "I am either std::variant or boost::variant\n";
}
};
You can see it live on godbolt.
The idea is to create a 'dispatcher' class, i.e., add another level of indirection. While this still takes some code, you don't have to reimplement it for every function:
#include <variant>
#include <iostream>
#include <boost/variant.hpp>
template <typename T, typename... Ts>
struct TypeCheckerOther
{
void operator()()
{
std::cout << "I am other type\n";
}
};
template <typename T, typename... Ts>
struct TypeCheckerVariant
{
void operator()()
{
std::cout << "I am either std::variant or boost::variant\n";
}
};
template <typename T>
struct TypeCheckerImpl
{
using type = TypeCheckerOther<T>;
};
template <typename... Ts>
struct TypeCheckerImpl<std::variant<Ts...>>
{
using type = TypeCheckerVariant<std::variant<Ts...>, Ts...>;
};
template <typename... Ts>
struct TypeCheckerImpl<boost::variant<Ts...>>
{
using type = TypeCheckerVariant<boost::variant<Ts...>, Ts...>;
};
template<typename T>
using TypeChecker = typename TypeCheckerImpl<T>::type;
int main()
{
TypeChecker<std::variant<int, float>>{}();
TypeChecker<boost::variant<int, float>>{}();
TypeChecker<int>{}();
}
std::eneble_if or std::same_as are cool, but in some cases old classic way looks better:
template <typename T>
struct is_any_variant : std::false_type {};
template <typename ...Ts>
struct is_any_variant<std::variant<Ts...>> : std::true_type {};
template <typename ...Ts>
struct is_any_variant<boost::variant<Ts...>> : std::true_type {};
template <typename T>
constexpr bool is_any_variant_v = is_any_variant<T>::value;
Live demo
I have classes foo_impl, bar_impl deriving from derived classes foo_derived, bar_derived, and a function that takes in a templated template-parameter:
#include <iostream>
template <typename T>
struct foo_base
{
T a;
};
template <typename T>
struct foo_derived: public foo_base<T>
{
};
struct foo_impl: public foo_derived<int>
{
};
template <typename T>
struct bar_base
{
T a;
};
template <typename T>
struct bar_derived : public bar_base<T>
{
};
struct bar_impl : public bar_derived<int>
{
};
template <typename T, template <class> typename Base>
void useBase(const Base<T>& Arg)
{
std::cout << Arg.a << std::endl;
}
int main()
{
foo_impl foo;
bar_impl bar;
useBase(foo); // ‘const Base<T>’ is an ambiguous base class of ‘foo_impl’
useBase(bar); // ‘const Base<T>’ is an ambiguous base class of ‘bar_impl’
}
Is it possible to specify that I want foo_base and bar_base to be the only types that can be passed to useBase(.)?
Edit: I tried introducing a concept in order to constrain the accepted types, but the compiler is still not happy.
template <typename T, template <class> typename Base>
concept is_base = !std::is_same<std::is_same<Base<T>, foo_base<T>>, std::is_same<Base<T>, bar_base<T>>>::value;
template <typename T, template <class> typename Base>
void useBase(const Base<T>& Arg) requires is_base<T, Base>
{
std::cout << Arg.a << std::endl;
}
As state in comment, overloading might be a possibility:
template <typename T, template <class> typename Base>
void useBaseImpl(const Base<T>& Arg)
{
std::cout << Arg.a << std::endl;
}
template <typename T>
void useBase(const foo_base<T>& arg)
{
useBaseImpl(arg);
}
template <typename T>
void useBase(const bar_base<T>& arg)
{
useBaseImpl(arg);
}
I tried to write a trait to check if a class has static function, but it always gives me the false. Could anyone tell me where is the problem?
#include <iostream>
template <template <typename...> class Trait, typename Ret, typename T>
struct is_detected : std::false_type
{
//This helps me to check that Get_t<A> is int
static_assert(std::is_same<Trait<T>, Ret>::value, "");
};
template <template <typename...> class Trait, typename T>
struct is_detected<Trait, Trait<T>, T> : std::true_type {};
class A {
public:
static int Get() {
std::cout << "I'm in get\n";
return 0;
}
};
template <typename T>
using Get_t = decltype(T::Get());
//template <typename T>
//using supports_Get = is_detected<Get_t, int, T>;
int main() {
std::cout << is_detected<Get_t, int, A>::value << std::endl;
return 0;
}
The following code works:
#include <iostream>
#include <type_traits>
namespace detail {
template <template <typename...> class Trait, typename V, typename T>
struct is_detected : std::false_type {};
template <template <typename...> class Trait, typename T>
struct is_detected<Trait, std::void_t<Trait<T>>, T> : std::true_type {};
}
class A {
public:
static int Get() {
std::cout << "I'm in get\n";
return 0;
}
};
template <typename T>
using Get_t = decltype(T::Get());
int main() {
std::cout << detail::is_detected<Get_t, void, A>::value << std::endl;
return 0;
}
Seems like there is not big difference between these examples.
Could anyone make me understand where is the problem of first code?
Note to C++17, 17.5.7.2 says, that
When a template-id refers to the specialization of an alias template,
it is equivalent to the associated type obtained by substitution of
its template-arguments for the template-parameters in the type-id of
the alias template. [ Note: An alias template name is never deduced. —
end note ]
This is one step process and subsequent template argument substitution never applies here.
Lets play with your first example:
template <template <typename> class Trait, typename Ret, typename T>
struct is_detected {};
struct A {
static int Get() {
return 0;
}
};
template <typename T>
using Get_t = decltype(T::Get());
template <typename T>
struct Get_t2 {
using Type = decltype(T::Get());
};
template <template <typename> class Trait, typename T>
struct is_detected<Trait, typename Trait<T>::Type, T> : std::true_type {};
template <template <typename> class Trait, typename T>
struct is_detected<Trait, Trait<T>, T> : std::true_type {};
Now you can see, that:
is_detected<Get_t2, int, A>::value // works, because it is normal type
is_detected<Get_t, int, A>::value // not, because it is alias
Interestingly your idea with void_t works because of C++17, 17.5.7.3
However, if the template-id is dependent, subsequent template argument
substitution still applies to the template-id
I recommend to use struct-based not alias-based templates whenever possible. Otherwise to make it work will be as hard as to kindle its self-immolation
Here is my C++ program code
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <typename T>
void func(const A<T>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
}
and it prints 1. But what I expect is the function call prints 2. Why B<int>b matches to const T& instead of const A<T>&. And how can I make it to match to const A<T>&?
Solution 1
You could use std::enable_if and templated template parameters to get a better match for the function, like:
#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <template <typename...> typename T, typename ...Args, typename = std::enable_if_t<std::is_base_of_v<A<Args...>, T<Args...>>>>
void func(const T<Args...>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
func(5);
}
However, this works only if A takes exactly the same template parameters as T. So if your B changes to ex.
template <typename T, typename U>
struct B : A<T> {}
this won't work anymore.
Solution 2
Based on Yakk's answer you can create a type trait that is more generic. This solution has no restrictions to its template parameters, like solution 1 does.
namespace detail
{
template <template <typename...> typename Base>
struct template_base_detector
{
template <typename... Args>
constexpr std::true_type operator()(Base<Args...>*);
constexpr std::false_type operator()(...);
};
}
template <template <typename...> typename Base, typename T>
struct is_template_base_of
: decltype(std::declval<detail::template_base_detector<Base>>()((T*)nullptr)) {};
// since C++ 14
template <template <typename...> typename Base, typename T>
constexpr bool is_template_base_of_v = is_template_base_of<Base, T>::value;
Depending on your c++ version, you can use different approaches to utilize this trait.
C++ 17
Probably the most compact solution. Since C++ 17 constexpr if statements are allowed, which allows us to define just a single func:
template <typename T>
void func(const T& t)
{
if constexpr (is_template_base_of_v<A, T>)
cout << 2 << endl;
else
cout << 1 << endl;
}
C++ 11 and 14
We have to fall back to tag dispatch:
namespace detail
{
template <typename T>
void func(std::true_type, const T& t)
{
std::cout << 2 << endl;
}
template <typename T>
void func(std::false_type, const T& t)
{
std::cout << 1 << endl;
}
}
template <typename T>
void func(const T& t)
{
detail::func(is_template_base_of<A, T>{}, t);
}
Tag dispatching.
First, we write a trait to detect if something has a template as a base:
namespace details {
template<template<class...>class Z>
struct htb {
template<class...Ts>
constexpr std::true_type operator()(Z<Ts...>*){return {};}
constexpr std::false_type operator()(...){return {};}
};
}
template<template<class...>class Z, class X>
constexpr inline auto has_template_base = details::htb<Z>{}((X*)nullptr);
we can now use our new trait to tag dispatch:
namespace details{
template <typename T>
void func(std::true_type,const A<T>& t) {
std::cout<<"2"<<std::endl;
}
template <class T>
void func(std::false_type,const T& t) {
std::cout<<"1"<<std::endl;
}
}
template <typename T>
void func(const T& t) {
details::func(has_template_base<A,T>,t);
}
Live example.
The following example doesn't work:
#include <iostream>
template <int index, template <typename> class Head,
template <typename> class... Tail>
struct TemplateTupleElement {
template <typename T>
using Type =
typename TemplateTupleElement<index - 1, Tail...>::template Type<T>;
};
template <template <typename> class Head, template <typename> class... Tail>
struct TemplateTupleElement<0, Head, Tail...> {
template <typename T>
using Type = Head<T>;
};
template <typename T>
class Dummy {
};
template <template <typename> class T>
class TemplateDummy {
public:
static void print() {
std::cout << "Template" << std::endl;
}
};
template <>
class TemplateDummy<Dummy> {
public:
static void print() {
std::cout << "Specialization" << std::endl;
}
};
int main(int argc, char* argv[]) {
TemplateDummy<TemplateTupleElement<0, Dummy, Dummy>::Type>::print();
TemplateDummy<TemplateTupleElement<1, Dummy, Dummy>::Type>::print();
return 0;
}
I expected the output to be:
Specialization
Specialization
However, the output of the program compiled with g++ ((Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204):
Specialization
Template
And the output with Clang (version 3.4-1ubuntu3) is:
Template
Template
I can understand that internally the compiler represents the two template expressions differently:
Dummy
TemplateTupleElement<1, Dummy, Dummy>::Type
Why isn't the second expression "simplified" into the first expression by g++, and why doesn't Clang map the expression correctly with either tuple indices?