Why aren't template expressions simplified internally? - c++

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?

Related

Solving base class ambiguity for nested template parameter functions

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);
}

Detect whether a type is a vector of enum

I get an error:
error: default template arguments may not be used in partial specializations
in the following code:
#include <iostream>
#include <type_traits>
#include <vector>
enum class MyEnum
{
aaa,
bbb,
};
template<class T>
struct is_vector_enum
{
using type = T ;
constexpr static bool value = false;
};
template<class T, class std::enable_if<std::is_enum<T>::value>::type* = nullptr> // Error ....
struct is_vector_enum<std::vector<T>>
{
using type = std::vector<T> ;
constexpr static bool value = true;
};
int main()
{
std::cout << "is_vector_enum: " << is_vector_enum<std::vector<MyEnum>>::value << std::endl;
}
The purpose is to detect whether a type is a vector of enum.
How should I fix this code?
Your primary template and your specialization need to have the same number of template parameters. At the moment, your primary has 1:
template<class T>
struct is_vector_enum
and your specialization has 2:
template<class T, class std::enable_if<std::is_enum<T>::value>::type* = nullptr>
struct is_vector_enum<std::vector<T>>
The typical way to do this in C++17 is to provide a dummy 2nd template parameter to the primary, that defaults to void, to then let you do the SFINAE in the second parameter:
template <class T, class Enable=void>
struct is_vector_enum { /* ... */ };
template <class T>
struct is_vector_enum<std::vector<T>, std::enable_if_t<std::is_enum_v<T>>> { /* ... */ };
A different way entirely to do this would be:
template <typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
std::true_type impl(std::vector<T> const&);
template <typename T>
std::false_type impl(T const&);
template <typename U>
using is_vector_enum = decltype(impl(std::declval<T>()));
Note that the impl functions here are not defined, and are not intended to be invoked.
Specializations are allowed to have a different number of template parameters than the primary. In fact, this happens quite often. However, as the error indicates, you are not allowed to give any of them default arguments.
That aside, I prefer simplicity, when possible.
template <typename T>
struct is_vector_enum : std::false_type { };
template <typename T>
struct is_vector_enum<std::vector<T>> : std::is_enum<T> { };

Could anyone explain me why this trait does not work properly?

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

Template alias, Template specialization and Template Template parameters

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

Trouble with metafunction for identifying class-within-template-class

I have code with the following structure:
template <typename T>
struct Foo
{
struct Bar
{
int data;
};
};
I want to write metafunctions which will tell me if a type is either Foo or Bar. The first one is easy:
template <typename T>
struct is_foo : boost::mpl::false_
{};
template <typename T>
struct is_foo<Foo<T> > : boost::mpl::true_
{};
...
BOOST_MPL_ASSERT(( is_foo<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_foo<int> ));
However, the same approach does not work for Bar:
template <typename T>
struct is_bar : boost::mpl::false_
{};
template <typename T>
struct is_bar<typename Foo<T>::Bar> : boost::mpl::true_
{};
This code is rejected by the compiler. GCC says:
main.cpp:38:8: error: template parameters not used in partial specialization:
main.cpp:38:8: error: ‘T’
Oddly, clang will compile the code, but it issues a warning and the metafunction does not work (always false):
main.cpp:38:8: warning: class template partial specialization contains a template parameter that can not be deduced;
this partial specialization will never be used
struct is_bar<typename Foo<T>::Bar> : boost::mpl::true_
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:37:20: note: non-deducible template parameter 'T'
template <typename T>
^
Is there a workaround for this issue? A c++11-specific solution would be fine.
Here's a hideously inelegant solution to my own question, using TTI (http://svn.boost.org/svn/boost/sandbox/tti):
First, add a dummy tag to Bar:
template <typename T>
struct Foo
{
struct Bar
{
typedef void i_am_bar;
int data;
};
};
Next, use TTI to check for that tag:
BOOST_TTI_HAS_TYPE(i_am_bar);
template <typename T>
struct is_bar : boost::tti::has_type_i_am_bar<T>
{};
...
BOOST_MPL_ASSERT(( is_bar<Foo<int>::Bar> ));
BOOST_MPL_ASSERT_NOT(( is_bar<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_bar<int> ));
Yucky to be sure, but it satisfies my use-case.
The problem is that T is part of the name of the type Foo<T>::Bar, but it's not part of the structure of the type.
A possible solution would be to encode T in the structure of the type:
template<typename Outer, typename Inner> struct Nested: public Inner {
using Inner::Inner;
};
template<typename T> struct Foo {
struct BarImpl {
int data;
};
using Bar = Nested<Foo<T>, BarImpl>;
};
template <typename T> struct is_bar: std::false_type {};
template <typename T, typename U> struct is_bar<Nested<Foo<T>, U>>:
std::is_same<typename Foo<T>::Bar, Nested<Foo<T>, U>> {};
Testing:
static_assert(is_bar<Foo<int>::Bar>::value, "!");
static_assert(!is_bar<Foo<int>>::value, "!");
static_assert(!is_bar<int>::value, "!");
compilers are correct, the simple and easy to understand explanation is: they just don't want to substitute all possible types T just to realise is there a nested type bar inside of given template. More precise explanation you may find in a 'classic' (and well known I hope) book about templates: "C++ Templates - The Complete Guide".
fortunately C++11 helps you to do it even better! :)
#include <type_traits>
template <typename T>
struct has_nested_bar
{
template <typename W>
struct wrapper {};
template <typename C>
static std::true_type check(
const wrapper<C>*
, const typename C::Bar* = nullptr
);
template <class C>
static std::false_type check(...);
constexpr static bool value = std::is_same<
decltype(check<T>(nullptr))
, std::true_type
>::type::value;
typedef std::integral_constant<bool, value> type;
};
this metafucntion would check if a given type has nested Bar type (as far as I understant it was initial purpise of your is_bar).
template <typename T>
struct Foo
{
struct Bar
{
int data;
};
};
struct Bar {};
int main()
{
std::cout << has_nested_bar<Foo<int>>::value << std::endl;
std::cout << has_nested_bar<Bar>::value << std::endl;
return 0;
}
will output:
zaufi#gentop /work/tests $ ./has-nested-bar
1
0
later you may combine this metafunction with your is_foo to check that nested Bar actually is inside of a Foo...