问题
Assume you have a templated class
like this:
template <typename type>
class Object {
using length_t = unsigned int;
template <length_t length>
void put(type (&)[length]);
};
and you declared a put(...)
method in it like so.
How do you go about declaring that put(...)
method outside the class
?
Here's one approach someone might take:
/* ERROR: Doesn't match any declarations(?) */ template <typename type> template <typename Object<type>::length_t length> void Object<type>::put(type (&)[length]) {}
but this leads to a peculiar error
error: no declaration matches 'void Object<type>::put(type (&)[length])' note: candidate is: template <class type> template <unsigned int length> void Object<type>::put(type (&)[length])
Here's another way to declare the
put(...)
method such that it works:/* SUCCESS: But `length_t` alias isn't used */ template <typename type> template <unsigned int length> void Object<type>::put(type (&)[length]) {}
but the
length_t
type alias defined in theclass
isn't used.
How does one get the first definition to work so as to keep the use of the class
's features (like type aliases) consistent across its declaration & definitions, or is the second definition the only solution here?
回答1:
How does one get the first definition to work so as to keep the use of the class's features (like type aliases) consistent across its declaration & definitions,
I have to admit that I don't understand the error and I don't know how to fix it by changing the definition only. The error message is rather confusing (you should include it in the question).
... or is the second definition the only solution here?
No, it is not. If you are fine with having length_t
not as member then this might point you in the right direction:
template <template<typename> typename T>
struct length { using type = int; };
template <template<typename> typename T>
using length_t = typename length<T>::type;
template <typename> struct Object;
template <> struct length<Object> { using type = unsigned int; };
template <typename type>
class Object {
//using length_t = unsigned int;
template <length_t<Object> length>
void put(type (&)[length]);
};
template <typename type>
template <length_t<Object> length>
void Object<type>::put(type (&)[length]) {}
length
is a "template trait" (not sure if this term actually exists). Instead of having length_t
as member of Object
you need to provide a specialization for length<Object>
(and that needs a forward declaration of Object
). The int
base case is only for illustration. And if you like you can still add a member to Object
to alias length_t<Object>
.
Live Demo
回答2:
I think it's a compiler bug, or further more, a defect in standard.
your code actually has NO problem, and is accepted by MSVC. and if you put the definition inside the class, no compiler will think it's ill-formed.
I have posted a question that is similar to this. and I get the result that, CWG2, the ancient issue that nobody knows when it's posted, is still drafting, which means the match rule of out-of-definition is even unspecified. these weird mismatches are because of the different implementations of compilers.
and then, for how to avoid this problem, firstly you can put the definition inside the class. and if it depends on something defined behind the class definition and can not be defined inside, you can:
- make it independent: let
using length_t = unsigned int;
outside. - make it deducible when declaring: compilers may not know if
typename Object<type>::length_t
andlength_t
(inside the class) are the same type, althoughtypename Object<type>::length_t
is not needed to be deducible. because at the declaring moment, compiler can not ensure ifObject<type>
is specified and makelength_t
mismatched, in my mind. so as what @idclev 463035818 said,template<...> using length_t = unsigned int;
will make compiler easier to match this definition.
来源:https://stackoverflow.com/questions/64598755/template-class-type-alias-failing-substitution-in-member-declaration