Why is it disallowed for partial specialization in a non-type argument to use nested template parameters
I think a lot of it is historical. Non-type template parameters weren't originally allowed at all. When they were added, there were lots of limitations. As people tried different possibilities, and confirmed that they didn't cause problems, some of the limitations were removed.
Some of those original limitations remain for no particular reason beyond the fact that nobody bothered to work at changing them. Much like there, many of them can be worked around so removing them generally often wouldn't cause any particular difficulty. Mostly it comes down to a question of whether anybody cared enough about this particular case to write a paper about it.
(Partially) specializing a non-type template parameter of dependent type
See paragraph [temp.class.spec] 14.5.5/8 of the standard:
The type of a template parameter corresponding to a specialized
non-type argument shall not be dependent on a parameter of the
specialization. [ Example:template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error
template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error
—end example ]
The answer to your edit: the easiest workaround is to replace a non-type template parameter with a type one:
#include <type_traits>
template <typename T, typename U>
struct X_;
template <typename T, T N>
struct X_<T, std::integral_constant<T, N>> {};
template <typename T>
struct X_<T, std::integral_constant<T, 0>> {};
template <typename T, T N>
struct X : X_<T, std::integral_constant<T, N>> {};
C++ template specializations not working with nested types
Your nested alias could refer to any type, particularly in a specialisation:
template<typename T>
struct Nesting
{
template<typename U>
struct _Nested
{
};
template<typename U>
using Nested = _Nested<U>;
};
// Consider this specialisation:
template<>
struct Nesting<int>
{
template<typename U>
using Nested = float;
};
Now, clearly F<Nesting<int>::Nested<int>>::is_my_nested_class
should be the same as F<float>::is_my_nested_class
, however, how can the compiler deduce this for the latter case? That is, if I wrote:
static_assert(F<float>::is_my_nested_class, "not nested");
The compiler would need to see that F<float>
is the same as F<Nesting<int>::Nested<int>>
, even though the latter hasn't been instantiated. As it can't reasonably be expected to do so, the case is disallowed.
Why is partial specialization of a nested class template allowed, while complete isn't?
My guess as to why this happens: complete specializations are no longer "template classes/functions", they are are "real" classes/methods, and get to have real (linker-visible) symbols. But for a completely-specialized template inside a partially-specialized one, this would not be true.
Probably this decision was taken just to simplify the life of compiler-writers (and make life harder for coders, in the process :P ).
Split variadic parameter pack using template specialization
What exactly is going wrong, [...]
There are several issues so it may be easier to start with a simple base example that works (no perfect forwarding).
[...] and how might I build my map primitive in a way that avoid this?
You can separate the parameter packs:
- argument types are passed as template arguments for
Map
- item types are passed as template arguments for
Map::function
Here is a working example without perfect forwarding. It is not complete regarding the deduction of the argument types because of possible cv-qualifiers and ref-qualifiers.
#include <iostream>
template<class F, class... Args>
struct Map {
template<class... Items>
static void function(Args... args, Items... items) {
static constexpr auto f = F{};
// here comes a fold expression
// see https://en.cppreference.com/w/cpp/language/fold
( f(items, args...), ...); // "fold over comma operator"
}
};
////////////////////////////////////////////////////////////////////////////////
template<class F, class Ret, class Item, class... Args, class... ArgsItems>
void map_impl(Ret(F::*)(Item, Args...) const, ArgsItems... args_items) {
Map<F, Args...>::function(args_items...);
}
template<class F, class... ArgsItems>
void map(ArgsItems... args_items) {
map_impl<F>(&F::operator(), args_items...);
}
////////////////////////////////////////////////////////////////////////////////
struct print_x_m_plus_n {
void operator()(int x, int m, int n) const {
int y = x * m + n;
std::cout << y << std::endl;
}
};
int main() {
constexpr int m = 2;
constexpr int n = 1;
map<print_x_m_plus_n>(m, n, 0, 1, 2);
}
Output:
1
3
5
SFINAE tried with bool gives compiler error: template argument ‘T::value’ involves template parameter
Actually what you're doing is forbidden by section §14.5.4/9 which says,
A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier.
The trick could be using a type for second template parameter as well, encapsulating the non-type value, as described below:
template<bool b> struct booltype {};
template<typename T, typename B = booltype<true> >
struct Resolve
{
static const bool value = false;
};
template<typename T>
struct Resolve<T, booltype<T::my_value> >
{
static const bool value = true;
};
Now it compile fines.
partial specialization for iterator type of a specified container type
You can't deduce types left of a nesting ::
. Indeed, your question makes no sense. Consider this simpler counter-example:
template <typename> struct Foo;
template <> struct Foo<bool> { typedef float type; };
template <> struct Foo<char> { typedef float type; };
template <typename> struct DoesntWork;
template <typename T> struct DoesntWork<typename Foo<T>::type> { };
Now if I say DoesntWork<float>
, what should T
be?
The point is that there is no reason that any T
should exist for which Foo<T>::type
is a thing you want to match, and even if there were one, there's no reason why it would be unique.
Template argument specialization example from cpprefference.com doesn't work
This is a recent language change, and even the current releases of several compilers don't implement it yet. It's CWG issue 1315, which lists the status as "tentatively ready", though according to @bogdan in the comments, the change has already been accepted into the standard. Prior to that change, it was invalid for exactly the reason that your compiler shows in its error message.
Changing GCC's behaviour is on the GCC bug tracker as PR 77781.
c++, can I enable several trait class specializations using enable_if instead of copy-paste?
I find very difficult to understand what do you want; please, revise you question to check details (version
and revision
are the same? fieldTraits
and traitsClass
are the same?).
Anyway, if I understand correctly, you want define a specialization for
template <const field& T, revision R>
class fieldTraits;
when R
is val1
or val2
or val3
and, maybe, other specializations for single following values.
Supposing you have four revisions
enum class revision { ver1, ver2, ver3, ver4 };
You can declare fieldTraits
(as a struct
, to make it shorter) adding a bool
default template parameter that say if R <= revision::ver3
template <field const & T, revision R, bool = (R <= revision::ver3)>
struct fieldTraits;
Now you can develop a specialization for ver1
, ver2
and ver3
template <field const & T, revision R>
struct fieldTraits<T, R, true>
{ static constexpr size_t field_val = 1; };
and a specialization for ver4
template <field const & T>
struct fieldTraits<T, revision::ver4>
{ static constexpr size_t field_val = 2; };
The following is a simplified but full example
#include <iostream>
enum class revision { ver1, ver2, ver3, ver4 };
struct field
{
int thingone;
constexpr field (int i) : thingone(i)
{ }
};
template <field const & T, revision R, bool = (R <= revision::ver3)>
struct fieldTraits;
// version ver1, ver2, ver3 cases
template <field const & T, revision R>
struct fieldTraits<T, R, true>
{ static constexpr size_t field_val = 1; };
// version ver4 case
template <field const & T>
struct fieldTraits<T, revision::ver4>
{ static constexpr size_t field_val = 2; };
static constexpr field f{1};
int main ()
{
std::cout << "ver1: " << fieldTraits<f, revision::ver1>::field_val
<< std::endl;
std::cout << "ver2: " << fieldTraits<f, revision::ver2>::field_val
<< std::endl;
std::cout << "ver3: " << fieldTraits<f, revision::ver3>::field_val
<< std::endl;
std::cout << "ver4: " << fieldTraits<f, revision::ver4>::field_val
<< std::endl;
}
that print
ver1: 1
ver2: 1
ver3: 1
ver4: 2
-- EDIT --
Following an OP's comment, I propose a little different solution
template <field const & T, revision R, typename = std::true_type>
struct fieldTraits;
// version ver1, ver2, ver3 cases
template <field const & T, revision R>
struct fieldTraits<T, R, std::integral_constant<bool, (R <= revision::ver3)>>
{ static constexpr size_t field_val = 1; };
// version ver4 case
template <field const & T>
struct fieldTraits<T, revision::ver4>
{ static constexpr size_t field_val = 2; };
Related Topics
On How to Recognize Rvalue or Lvalue Reference and If-It-Has-A-Name Rule
How to Find Out If a Tuple Contains a Type
How to Use Setenv() to Export a Variable in C++
How to Extract the Mantissa of a Double
How to Overwrite Only Part of a File in C++
High Delay in Rs232 Communication on a Pxa270
Does This Type of Memory Get Allocated on the Heap or the Stack
Difference Between Cc, Gcc and G++
Why Does This Call the Default Constructor
Profiler for Visual Studio 2008, C++
What Encoding Does Std::String.C_Str() Use
Assembly Adc (Add with Carry) to C++
Is There a Limit of Stack Size of a Process in Linux
What Is the Use of 0-Length Array (Or Std::Array)
C++11 "Overloaded Lambda" with Variadic Template and Variable Capture
Why I Can Access Member Functions Even After the Object Was Deleted