First some code, then some context, then the question:
template <typename T> using id = T;
template <template <typename...> class F, typename... T>
using apply1 = F <T...>;
template <template <typename...> class F>
struct apply2
{
template <typename... T>
using map = F <T...>;
};
// ...
cout << apply1 <id, int>() << endl;
cout << apply2 <id>::map <int>() << endl;
Both clang 3.3 and gcc 4.8.1 compile this without error, applying the identity metafunction to int, so both expressions evaluate to a default int (zero).
The fact that id is a template <typename> while apply1, apply2 expect a template <typename...> did concern me in the first place. However, it is quite convenient that this example works because otherwise metafunctions like apply1, apply2 would have to be so much more involved.
On the other hand, such template aliases cause serious problems in real-world code that I cannot reproduce here: frequent internal compiler errors for gcc, and less frequent unexpected behavior for clang (only in more advanced SFINAE tests).
After months of trial and error, I now install and try the code on the (experimental) gcc 4.9.0, and here comes the error:
test.cpp: In instantiation of ‘struct apply2<id>’:
test.cpp:17:22: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using id = T’
using map = F <T...>;
^
Ok, so it seems this code was not valid all this time, but gcc crashed in various ways instead of reporting the error. Interestingly, while apply1, apply2 appear to be equivalent, the error is only reported for apply2 (which is much more useful in practice). As for clang, I really cannot say.
In practice, it seems I have no other way than to go along with gcc 4.9.0 and correct the code, even though it will become much more complex.
In theory, I would like to know what the standard says: is this code valid? If not, is the use of apply1 invalid as well? or only apply2?
EDIT
Just to clarify that all problems I've had so far refer to template aliases, not template structs. For instance, consider the following modification:
template <typename T> struct id1 { using type = T; };
// ...
cout << typename apply1 <id1, int>::type() << endl;
cout << typename apply2 <id1>::map <int>::type() << endl;
This compiles fine and prints 0 in both cases, on clang 3.3, gcc 4.8.1, gcc 4.9.0.
In most cases, my workarounds have been introducing an intermediate template struct before the alias. However, I am now trying to use metafunctions to parametrize generic SFINAE tests and in this case I have to use aliases directly, because structs should not be instantiated. Just to get an idea, a piece of the actual code is here.
ISO C++11 14.3.3/1:
A template-argument for a template template-parameter shall be the name of a class template or an alias template, expressed as id-expression.
Plus I don't see any special exceptions for variadic template template parameters.
On the other hand, such template aliases cause serious problems in real-world code that I cannot reproduce here: frequent internal compiler errors for gcc, and less frequent unexpected behavior for clang (only in more advanced SFINAE tests).
Root of problems can be in other places. You should try to localize code which causes internal compiler error - just remove unrelated parts one by one (or use some kind of binary search, i.e. divide-and-conquer) - and check if error is still here on each stage.
As for GCC 4.9.0 error, try to change
template <typename... T>
using map = F <T...>;
to
template <typename... U>
using map = F <U...>;
Maybe this would help to understand what GCC sees.
Related
I am failing to understand how the requires keyword works inside a nested template.
The code below can be compiled on the latest versions of MSVC and gcc (using /std:c++latest and -std=c++2a, respectively).
Is the requires simply discarded in scenarios like this? Should I not use it this way?
#include <type_traits>
template <
template < typename >
requires (false) // Should not this stop compilation?
typename Wrapper >
using Test = Wrapper < int >;
template < typename >
struct S
{
};
int main() {
Test < S > var;
return 0;
}
I think the compilers are not implementing this correctly and you are correct that it should fail to compile.
In [temp.names]/7 it says that a template-id formed from a template template parameter with constraints must satisfy these constraints if all template arguments are non-dependent.
You are giving Wrapper only one argument, namely int which is not dependent. Therefore the compiler should check whether Wrapper<int> satisfies the constraint requires(false) of Wrapper. This check should fail.
I am not completely sure that requires(false) specifically is not IFNDR, because there are some similar rules forbidding e.g. templates which can never be instantiated, but the compilers seem to behave the same way if a non-trivial constraint is used.
Clang complains that the requires clause is a syntax error in that position, but I don't see any reason for that.
MSVC actually handles for example the following variation using a type constraint instead of a requires-clause as expected:
template<
template<std::same_as<float> T>
typename Wrapper>
using Test = Wrapper<int>;
but does not reject as expected if a requires-clause is used:
template<
template<typename T>
requires std::same_as<float, T>
typename Wrapper>
using Test = Wrapper<int>;
Interestingly Clang crashes with an ICE on the former.
I work with template template parameters and want to construct new types based on template template parameters defined at another location. This sometimes works, but I think I miss something fundamentally, conceptually.
In the below minimal (I hope not too much distorted) example I expect the types of Example<GerericTemplate> and MakeAnotherX<Example<GenericTemplate>>::type to be identical, but they aren't always.
struct Something {};
template <template <typename> typename TemplateParameter>
struct Example {
template <typename T>
using InputTemplate = TemplateParameter<T>;
using FullType = TemplateParameter<Something>;
private:
FullType fData;
};
template <typename T>
struct GenericTemplate {};
// this works with (recent) GCC, but the static_assert below fails with clang
template <typename ExistingType,
template <typename> typename _InputTemplate
= ExistingType::template InputTemplate>
struct MakeAnother1 {
using type = Example<_InputTemplate>;
};
// this compiles, but static_assert below fails for both (recent) gcc and clang
template <typename ExistingType>
struct MakeAnother2 {
template <typename T> using _InputTemplate
= typename ExistingTemplate::template InputTemplate<T>;
using type = Example<_InputTemplate>;
};
#include <type_traits>
int main() {
using type1 = Example<GenericTemplate>;
static_assert(std::is_same<type1, MakeAnother1<type1>::type >::value, "this is not the same");
static_assert(std::is_same<type1, MakeAnother2<type1>::type >::value, "this is not the same");
return 0;
}
this example in godbolt
So far I am using gcc and clang (-std=c++17) for testing (and production), but in general I just wish to have standard compliant code. I really would like to be able to to reuse template template parameters in different context and classes as described in the minimal example above.
What is the correct way to achieve this in a robust manner? Also: is the fact that MakeAnother1 works with GCC a feature or a bug of GCC (and vice versa for clang)?
The "real code" is significantly more complex, so in particular I want those "Make" helper structs to work on new types.
Update
An earlier conceptually identical version of this question was asked about 6 years ago see old post. The news is that today this work on gcc version 5 and beyond but still no other compiler seems to support it. Unfortunately the discussion on CWG seems to have stalled on this point and could benefit from a reminder?
At this stage: after this update, if you prefer we can of course close this report here now. Thanks for all help!
One for the language lawyers....
I'm playing around with SFINAE and TMP, trying to get a deeper understanding.
Consider the following code, a naive implementation of std::is_default_constructible
#include <type_traits>
template <typename T, typename = void> struct is_default_constructable : std::false_type {};
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
class NC { NC(int); }; // Not default constructable
#include <iostream>
int main(int, char **)
{
std::cout << "int is_default_constructible? " << is_default_constructable<int>() << std::endl;
std::cout << "NC is_default_constructible? " << is_default_constructable<NC>() << std::endl;
}
This compiles fine, but doesn't actually work, it returns false for all types.
For the NC case, this is as I'd expect, T() is not well-formed so that specialization is discarded due to SFINAE and the primary template (false_type) is used. But for the int case, I'd expect the specialization to be used as decltype(T()) is valid and equivalent to T.
If, based on the actual code in <type_traits>, I change the specialization to
template <typename T> using wrap = void;
template <typename T> struct is_default_constructable<T, wrap<decltype(T())> > : std::true_type {};
(i.e. wrap the second template parameter in a mockup of std::void_t<> which forces the second type to be void), this works as expected.
Even more curious, variations of this scheme using types other than void as the default type in the primary template or wrap<> also fail, unless the two types are the same.
Can someone explain why the type of wrap<> and the second template argument default type need to be the same in order for the specialization to be selected?
(I'm using "g++ -Wall --std=c++17" with g++ version 6.3, but I think this is not compiler-related.)
This is not a consequence of SFINAE or partial specialization ordering, but due to the use of default template parameters. Informally, the reason is that the application of default template parameters happens before the search for template definitions, including possible specializations.
So in the above case, the code that says is_default_constructable<int> is actually requesting to instantiate a template is_default_constructable<int, void> after applying the default second parameter. Then possible definitions are considered.
The "primary" template definition clearly matches and is included.
The given partial specialization
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
is actually defining is_default_constructable<int, int> which does not match the requested is_default_constructable<int, void> so the specialization is ignored, even if the substitution succeeds.
This leaves the primary definition (inheriting false_type) as the only viable definition so it is chosen.
When the specialization has wrap<> (or std::void_t<>) to force the second arg to a void, the specialization is defining is_default_constructable<int, void> which matches the request. This definition (asuming the substitution succeeds, i.e. T() is well formed) is more specialized than the primary definition (according to super-complicated rules for ordering specializations), so it is chosen.
As an aside, the above naive implementations probably don't work as expected when T is a reference type or other corner cases, which is a good reason to use the standard library versions of all this. Them standards committee people are way smarter than I am and have already thought of all these things.
This answer and this answer to somewhat-related questions have more detailed information that set me right.
And, yes, I can't spell constructible, assuming that's even a word. Which is another good reason to use the standard library.
#include <type_traits>
template <typename T>
struct C;
template<typename T1, typename T2>
using first = T1;
template <typename T>
struct C<first<T, std::enable_if_t<std::is_same<T, int>::value>>>
{
};
int main ()
{
}
Results of compilation by different compilers:
MSVC:
error C2753: 'C': partial specialization cannot match argument list for primary template
gcc-4.9:
error: partial specialization 'C' does not specialize any template arguments
clang all versions:
error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list
gcc-5+:
successfully compiles
And additionaly I want to point out that trivial specialization like:
template<typename T>
struct C<T>
{
};
successfully fails to be compiled by gcc. So it seems like it figures out that specialization in my original example is non-trivial. So my question is - is pattern like this explicitly forbidden by C++ standard or not?
The crucial paragraph is [temp.class.spec]/(8.2), which requires the partial specialization to be more specialized than the primary template. What Clang actually complains about is the argument list being identical to the primary template's: this has been removed from [temp.class.spec]/(8.3) by issue 2033 (which stated that the requirement was redundant) fairly recently, so hasn't been implemented in Clang yet. However, it apparently has been implemented in GCC, given that it accepts your snippet; it even compiles the following, perhaps for the same reason it compiles your code (it also only works from version 5 onwards):
template <typename T>
void f( C<T> ) {}
template <typename T>
void f( C<first<T, std::enable_if_t<std::is_same<T, int>::value>>> ) {}
I.e. it acknowledges that the declarations are distinct, so must have implemented some resolution of issue 1980. It does not find that the second overload is more specialized (see the Wandbox link), however, which is inconsistent, because it should've diagnosed your code according to the aforementioned constraint in (8.2).
Arguably, the current wording makes your example's partial ordering work as desired†: [temp.deduct.type]/1 mentions that in deduction from types,
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values […] that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
Now via [temp.alias]/3, this would mean that during the partial ordering step in which the partial specialization's function template is the parameter template, the substitution into is_same yields false (since common library implementations just use a partial specialization that must fail), and enable_if fails.‡ But this semantics is not satisfying in the general case, because we could construct a condition that generally succeeds, so a unique synthesized type meets it, and deduction succeeds both ways.
Presumably, the simplest and most robust solution is to ignore discarded arguments during partial ordering (making your example ill-formed). One can also orientate oneself towards implementations' behaviors in this case (analogous to issue 1157):
template <typename...> struct C {};
template <typename T>
void f( C<T, int> ) = delete;
template <typename T>
void f( C<T, std::enable_if_t<sizeof(T) == sizeof(int), int>> ) {}
int main() {f<int>({});}
Both Clang and GCC diagnose this as calling the deleted function, i.e. agree that the first overload is more specialized than the other. The critical property of #2 seems to be that the second template argument is dependent yet T appears solely in non-deduced contexts (if we change int to T in #1, nothing changes). So we could use the existence of discarded (and dependent?) template arguments as tie-breakers: this way we don't have to reason about the nature of synthesized values, which is the status quo, and also get reasonable behavior in your case, which would be well-formed.
† #T.C. mentioned that the templates generated through [temp.class.order] would currently be interpreted as one multiply declared entity—again, see issue 1980. That's not directly relevant to the standardese in this case, because the wording never mentions that these function templates are declared, let alone in the same program; it just specifies them and then falls back to the procedure for function templates.
‡ It isn't entirely clear with what depth implementations are required to perform this analysis. Issue 1157 demonstrates what level of detail is required to "correctly" determine whether a template's domain is a proper subset of the other's. It's neither practical nor reasonable to implement partial ordering to be this sophisticated. However, the footnoted section just goes to show that this topic isn't necessarily underspecified, but defective.
I think you could simplify your code - this has nothing to do with type_traits. You'll get the same results with following one:
template <typename T>
struct C;
template<typename T>
using first = T;
template <typename T>
struct C<first<T>> // OK only in 5.1
{
};
int main ()
{
}
Check in online compiler (compiles under 5.1 but not with 5.2 or 4.9 so it's probably a bug) - https://godbolt.org/g/iVCbdm
I think that int GCC 5 they moved around template functionality and it's even possible to create two specializations of the same type. It will compile until you try to use it.
template <typename T>
struct C;
template<typename T1, typename T2>
using first = T1;
template<typename T1, typename T2>
using second = T2;
template <typename T>
struct C<first<T, T>> // OK on 5.1+
{
};
template <typename T>
struct C<second<T, T>> // OK on 5.1+
{
};
int main ()
{
C<first<int, int>> dummy; // error: ambiguous template instantiation for 'struct C<int>'
}
https://godbolt.org/g/6oNGDP
It might be somehow related to added support for C++14 variable templates. https://isocpp.org/files/papers/N3651.pdf
I have a boost::variant of several ranges. In this context, a range is just a std::pair<It, It>, where It is an iterator. I use this to store ranges of iterators satisfying certain properties.
Since I don't know the iterator types, I use a little template meta-programming to obtain the first_type of the std::pair, since I need a second boost::variant containing a single iterator (corresponding to some active element of that type).
The following code is simplified to help with the question, but consider that I have an unknown number of ranges in my RangeVariant (which means I can't create it manually, as I can do for this particular case).
#include <utility>
#include <vector>
#include <boost/variant.hpp>
template <class A, template <typename...> class B>
struct FirstTypeVariantImpl;
template <template <typename...> class A, typename... Pair, template <typename...> class B>
struct FirstTypeVariantImpl<A<Pair...>, B> /*! specialization */
{
using type = B<typename Pair::first_type...>;
};
template <class A, template <typename...> class B>
using FirstTypeVariant = typename FirstTypeVariantImpl<A, B>::type;
int main()
{
using Container = std::vector<int>;
using Range = std::pair<Container::iterator, Container::iterator>;
using RangeVariant = boost::variant<Range>;
using IteratorVariant = FirstTypeVariant<RangeVariant, boost::variant>;
};
The above program compiles correctly with gcc, but fails with clang. The error I get is the following:
program.cpp:12:29: error: incomplete type 'boost::detail::variant::void_' named in nested name specifier
using type = B<typename Pair::first_type...>;
^~~~~~
program.cpp:16:1: note: in instantiation of template class 'FirstTypeVariantImpl<boost::variant<std::pair<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > >, boost::detail::variant::void_, ..., boost::detail::variant::void_>, variant>' requested here
using FirstTypeVariant = typename FirstTypeVariantImpl<A, B>::type;
^
program.cpp:23:29: note: in instantiation of template type alias 'FirstTypeVariant' requested here
using IteratorVariant = FirstTypeVariant<RangeVariant, boost::variant>;
^
../../../include/boost/variant/variant_fwd.hpp:193:8: note: forward declaration of 'boost::detail::variant::void_'
struct void_;
^
So, it seems clang is attempting to obtain the first_type of boost::detail::variant::void_, but somehow gcc recognizes it and ignores it. Something similar happens if I obtain the type for the first element using the <tuple> header:
using type = B<typename std::tuple_element<0, Pair>::type...>;
The error after this change is different, but again related to clang trying to apply the operation to boost::detail::variant::void_:
program.cpp:13:34: error: implicit instantiation of undefined template 'std::tuple_element<0, boost::detail::variant::void_>'
using type = B<typename std::tuple_element<0, Pair>::type...>;
I'm using boost 1.57.0, gcc 4.8.3 and clang 3.6.0, always using -std=c++11 with -Wall -Werror -Wextra flags. Using other versions of either of these is not an option :-(
Any help would be appreciated. I don't even know whether this is a bug in clang or boost, or even in gcc, if my usage is incorrect. Thanks in advance for your help.
We agree on that void_ is part of boost::variant's pre-variadic template workaround (every instantiation is boost::variant<MandatoryType, ⟪boost::detail::variant::void_ ⨉ 𝖫𝖨𝖲𝖳_𝖲𝖨𝖹𝖤 ⟫>).
Now, the thing is that using metashell I found out there exists at least one version of boost::variant that does not use this workaround.
Looking around, I found that there was a bug recently fixed about how boost libs do not recognize clang's variadic template capability correctly.
To answer your question: gcc compiles because boost libs recognize variadic template availability, while missing clang's. This results in void_ failing to be instantiate in your meta-programming tangle as this struct has been declared, but not defined.
The reason this doesn't work is that boost::variant isn't implemented the way you think it is.
boost::variant like all of boost is compatible with C++03, before the time when there were variadic templates.
As a result, boost::variant has to work around the lack of that language feature, by imposing a maximum number of variants and using only C++03 template features.
The way they do this is, the template has 20 template arguments, and they all have a default value of boost::variant::detail::void_.
Your variadic capture is catching those extra parameters, just the same way that if you tried to capture all the parameters to std::vector you would get your type, also an allocator, etc., even if you didn't explicitly specify an allocator.
The work arounds I can think of off-hand are,
1) Don't use boost::variant, use a C++11 variant based on variadic templates. There are many implementations floating around.
2) Use boost variant, but also create a type-trait that permits you recover the original parameter pack from a typelist. You would have to make sure that every time you instantiate it, you also create an entry in the type trait, but you can use a macro to make sure that happens.
3) There may be a way to make boost::variant use an implementation based on variadic templates? But I'm not sure of this, I would have to review the docs. If there is then it means there is some preprocessor define you can use to force this.
Edit: The macro is this actually:
http://www.boost.org/doc/libs/1_60_0/doc/html/BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES.html
So in recent versions of boost, you have to request explicitly not to have the variadic implementation, unless you are on C++03 presumably?
You might want to explicitly check if something in one of your headers is defining this for some reason.
Although both Chris' and Felipe's contributions answer my question partially (thanks guys!), here is an update that actually compiles with the Boost and clang versions I mentioned.
First, update the specialization of FirstTypeVariant so that it obtains the type from another structure instead of directly obtaining T::first_type:
template <template <typename...> class A, typename... Pair, template <typename...> class B>
struct FirstTypeVariantImpl<A<Pair...>, B> /*! specialization */
{
using type = B<typename ObtainFirstType<Pair>::type...>;
};
Then, specialize the ObtainFirstType struct so that it returns the iterator type for std::pair<T, T> (remember that in my use case, T is an iterator).
template <typename T>
struct ObtainFirstType
{
using type = T;
};
template <typename T>
struct ObtainFirstType<std::pair<T, T>>
{
using type = T;
};
Now, this will compile and work, but there's a caveat. The number of elements of the variant with clang will always be 20, so any algorithm depending on that might change its behavior. We can count them like this:
template <typename... Ts>
struct VariantSize
{
static constexpr std::size_t size = 0;
};
template <typename... Ts>
struct VariantSize<boost::variant<Ts...>>
{
static constexpr std::size_t size = sizeof...(Ts);
};
In my example, I created a variant with 3 elements, which I then counted:
int main()
{
using ContainerA = std::vector<int>;
using ContainerB = std::vector<double>;
using ContainerC = std::vector<bool>;
using RangeA = std::pair<ContainerA::iterator, ContainerA::iterator>;
using RangeB = std::pair<ContainerB::iterator, ContainerB::iterator>;
using RangeC = std::pair<ContainerC::iterator, ContainerC::iterator>;
using RangeVariant = boost::variant<RangeA, RangeB, RangeC>;
using IteratorVariant = FirstTypeVariant<RangeVariant, boost::variant>;
std::cout << "RangeVariant size : " << std::to_string(VariantSize<RangeVariant>::size) << std::endl;
std::cout << "IteratorVariant size : " << std::to_string(VariantSize<IteratorVariant>::size) << std::endl;
};
The output with GCC is
RangeVariant size : 3
IteratorVariant size : 3
while the output with CLANG is the following:
RangeVariant size : 20
IteratorVariant size : 20