is it possible to check if T is a primary template? [duplicate] - c++

Let's say I have this:
template<typename T>
class my_template {...};
Now, users are expected to be able to specialize my_template for their own types. And they will pass those types to some API functions of mine, which will use properties of my_template<T> to do stuff.
So at some point in my code, I have a my_template<T>. I want a meta-function of some kind which takes my_template<T> and results in a compile-time value of true if my_template<T> is a user-provided specialization (partial or explicit) and false if it isn't.
The most obvious solution is to shove a private member alias, or some other concept-detectable private declaration, into the primary my_template definition, and rely on that not being present in user-provided specializations. However, a user could forge an explicit specialization by providing an appropriate definition. So this isn't foolproof.
This question is not sophistry. The C++20 specification has a meta-function ITER_CONCEPT(I), which has different internal behavior based on whether std::iterator_traits<I> is from the primary template or a user-provided specialization. Of course, being the standard library, they can create an identifier prefixed with __ as a member of the primary template and thereby declare any attempted forgery from user-space to be undefined behavior.
Is this a thing only a compiler/standard library implementation can do in a foolproof, or is it possible to do this in C++20?

The most obvious solution is to shove a private member alias, or some other concept-detectable private declaration, into the primary my_template definition, and rely on that not being present in user-provided specializations. However, a user could forge an explicit specialization by providing an appropriate definition. So this isn't foolproof.
That's basically it, yep. For instance, libstdc++'s iterator traits has its primary class template inherit from a hidden base class template, and then checks for inheritance from that base.
Yes, a user could forge an explicit specialization by providing an appropriate definition - but, like, don't. That isn't something you would do by accident, that's explicitly and pointlessly malicious, and the typical saying is that the library and the language defend against Murphy, not Machiavelli.
With Modules, you can make it even harder for the user to be explicitly malicious by exporting the primary template but not actually exporting the base class that you're using to check if the class template was specialized:
export module std;
namespace std {
template <typename T> struct __iterator_traits { };
template <typename T>
export struct iterator_traits : __iterator_traits { };
}
Now the user can't even name std::__iterator_traits in order to break the library. Perhaps reflection will still give them a way to do so (assuming those things are still accessible), but now they'd really be jumping through a lot of hoops.
Speaking of Reflection, part of the currently-specified API (P1240) includes the functions is_specialization, is_partial_specialization, and is_explicit_specialization. If those mean what I think they mean, then when we eventually get reflection, then the library wouldn't have do use these magic base / magic member hacks and could just directly check to see if the provided specialization was a partial or explicit specialization or not. I figure the library should probably add is_primary_specialization for completeness.

Related

Template class forward declaration with default argument [duplicate]

Suppose we have a class template with default template parameter:
template <typename T = int>
class Foo {};
We can omit angle brackets when creating a variable inside a function:
int main()
{
Foo a; // gets properly deduced as Foo<int>
}
But we can't do that for member variables:
struct S
{
Foo a; // Deduce Foo<int>
};
We can't have derivative types such as this:
Foo* ptr; // Foo<int>*
Foo& ref; // Foo<int>&
int Foo::* mem_ptr; // int Foo<int>::*
std::function<Foo(const Foo&)> fn; // std::function<Foo<int>(const Foo<int>&)>
We can't accept parameters and return them:
Foo Bar(const Foo&); // Foo<int> (*)(const Foo<int>&)
Why? Is this considered a bug in the standard? Is there a proposal to fix it? Are there any actual problems with omitting angle brackets?
My use case:
I have a class template which provides default argument. The template parameter is an expert-only feature that I myself never use but it is there for those 1% of experts who want that total flexibility. Now for other 99% I want to hide the fact that Foo is actually a class template but it doesn't work because users have to type Foo<> when declaring it as a member variable, current solution is this:
template <typename T = int>
class BasicFoo {};
using Foo = BasicFoo<>;
But it complicates implementation code and is not elegant at all.
Is this considered a bug in the standard?
No.
Templates are a named construct which generates another construct (classes/functions/variables) based on a set of parameters. The name of a template is not the name of the construct which it generates. The name of a template is just the name of the template; to name the thing the template generates, you must provide the template parameters.
Foo is the name of a template; Foo<> is the name of a class generated by that template and its associated template parameters.
There are a couple of places where C++ allows a template to be used in such a way that its parameters are deduced from a sequence of expressions. But these are very specific places, created for convenience purposes. They do not exist for the purpose of hiding the fact that a name represents a template rather than the generated construct.
Is there a proposal to fix it?
There is nothing broken to fix. And there are at present no proposals adding changes in this way.
Are there any actual problems with omitting angle brackets?
Define "actual problem". Is it theoretically possible to have the language altered so that, if all of a template's parameters are defaulted, the name of the template can be used without template parameters to simultaneously mean the template and the thing the template generates?
It is probably possible. But it would be complicated to specify. You would need a serious spec-doctor, one who understands the C++ grammar at a deep level, to know for sure whether it is possible, and what exactly would need to be changed to do it.
But at the end of the day, it would only ever be useful for a small, select set of templates: templates that have default values for all of its parameters. The real question is whether that is a common enough case to be worth the effort.
I don't consider myself a language expert, but my stance would be that the problem your proposal tries to tackle is solved much simpler in the same way std::(basic_)string does it.
We have
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
> class basic_string;
and then a set of typedefs for "non-expert" users, such as std::string for std::basic_string<char>.
If an expert user wants to make use of the other template parameters, they can themselves define a type alias, which is nice and coherent with the above. Moreover, this cleanly separates the template from the types that are created from it.
Your suggestion of allowing templates with defaults for all parameters to be named by MyTemplate alone, instead of requiring MyTemplate<>, or making use of using MyTemplate = MyBasicTemplate<>;, has the following issues:
It complicates the grammar and specification of the language. You need to touch the allowed syntax of all the contexts mentioned in your question, adding the ability to use a template name where a type name would be expected, but only if the relevant template has default values for all template parameters. And if you don't change all of them, you introduce weirdly inconsistent behavior.
There is some overlap between your suggestion and CTAD, but CTAD is decidedly about reducing type verbosity for initialization. CTAD offers significant comfort within its scope and is extensible through deduction guides, whereas your proposal's syntactic sugar is only relevant in a tiny usage niche, with much smaller benefits.
There is the danger of accidentally using the wrong template parameters (did you mean the default template parameters or did you just forget to specify the ones you wanted?). Even if that is not a concern in your use case, the standard would have to concern itself with that potential issue.
There is also the danger of your suggestion conflicting with deduction guides. Who should win?
Your problem is easily and conveniently solved with existing language tools (see above). I disagree that this "complicates" implementation code (complexity is literally only increased by a single typedef/using, (re)naming your template is absolutely trivial work) or that it is inelegant.
Overall, the problem you intend to solve (saving library implementers a using, or users a <> (or using), exclusively for all-defaulted templates) is fringe at best and will not be a sufficient motivation for significantly altering several core aspects the language. That's my prediction at least.

Detect if a type is a specialization from the primary template or a user-provided specialization

Let's say I have this:
template<typename T>
class my_template {...};
Now, users are expected to be able to specialize my_template for their own types. And they will pass those types to some API functions of mine, which will use properties of my_template<T> to do stuff.
So at some point in my code, I have a my_template<T>. I want a meta-function of some kind which takes my_template<T> and results in a compile-time value of true if my_template<T> is a user-provided specialization (partial or explicit) and false if it isn't.
The most obvious solution is to shove a private member alias, or some other concept-detectable private declaration, into the primary my_template definition, and rely on that not being present in user-provided specializations. However, a user could forge an explicit specialization by providing an appropriate definition. So this isn't foolproof.
This question is not sophistry. The C++20 specification has a meta-function ITER_CONCEPT(I), which has different internal behavior based on whether std::iterator_traits<I> is from the primary template or a user-provided specialization. Of course, being the standard library, they can create an identifier prefixed with __ as a member of the primary template and thereby declare any attempted forgery from user-space to be undefined behavior.
Is this a thing only a compiler/standard library implementation can do in a foolproof, or is it possible to do this in C++20?
The most obvious solution is to shove a private member alias, or some other concept-detectable private declaration, into the primary my_template definition, and rely on that not being present in user-provided specializations. However, a user could forge an explicit specialization by providing an appropriate definition. So this isn't foolproof.
That's basically it, yep. For instance, libstdc++'s iterator traits has its primary class template inherit from a hidden base class template, and then checks for inheritance from that base.
Yes, a user could forge an explicit specialization by providing an appropriate definition - but, like, don't. That isn't something you would do by accident, that's explicitly and pointlessly malicious, and the typical saying is that the library and the language defend against Murphy, not Machiavelli.
With Modules, you can make it even harder for the user to be explicitly malicious by exporting the primary template but not actually exporting the base class that you're using to check if the class template was specialized:
export module std;
namespace std {
template <typename T> struct __iterator_traits { };
template <typename T>
export struct iterator_traits : __iterator_traits { };
}
Now the user can't even name std::__iterator_traits in order to break the library. Perhaps reflection will still give them a way to do so (assuming those things are still accessible), but now they'd really be jumping through a lot of hoops.
Speaking of Reflection, part of the currently-specified API (P1240) includes the functions is_specialization, is_partial_specialization, and is_explicit_specialization. If those mean what I think they mean, then when we eventually get reflection, then the library wouldn't have do use these magic base / magic member hacks and could just directly check to see if the provided specialization was a partial or explicit specialization or not. I figure the library should probably add is_primary_specialization for completeness.

C++ template explicit specialization - calling existing member function

I'm using explicit template specialization to initialize a std::vector with information but only for a specific type of std::vector, thus the explicit specialization. Within the constructor, if I try to call push_back or any other existing function in std::vector, compilation fails. What is the problem and how do I fix it?
simplified example:
namespace std
{
template<>
class vector<int>
{
public:
vector(void)
{
int value = 5;
push_back(value);
}
};
}
compiler message:
In constructor 'std::vector<int>::vector()':
error: 'push_back' was not declared in this scope
push_back(value);
^
Explicit specializations are completely different classes that are separate from the primary template. You have to rewrite everything.
In normal situations where you control the primary template, you would typically have some sort of common base class or base class template to collect common structures.
With a given library, it is generally a very bad idea to add specializations (unless the library explicitly says it's OK). With the C++ standard library, this is outright undefined behaviour.
(The main problem is that other translation units may be using the template instantiation which you're specializing without seeing your specialization, which violates the one-definition rule.)
Template specializations are unrelated types from both the primary template and any other specialization. It is unclear what you are attempting to do, as it is also illegal to provide specializations of templates in the std namespace unless the specialization uses your own user defined type.
If you can explain the problem to solve, you might get other options, like specializing a member function rather than the template itself...

Use typedef/using from templated base class in derived class

In accessing a using from a base class with a templated base class, I've run into an issue with verbosity if nothing else. In the below code, the derived class attempts to use the my_type from its base class.
template <typename T>
class Base {
public:
using mytype = T;
};
template <typename T>
class Derived : public Base<T>{
public:
// using the base's mytype here
static typename Base<T>::mytype func() { return 0;}
};
However, in practice I'm finding this to be an awful lot of characters for what seems like it should be simpler. if the Base class was not templated then it wouldn't require the <T> or the typename (obviously).
In my real issue, I have a great deal of classes deriving from the base and I want to simplify this if possible. What I have at the moment is like the next example, where I'm just adding an additional using to get the type from the base class, but it feels like an extra layer that I shouldn't need to have.
template <typename T>
class Derived : public Base<T>{
public:
using base_type = typename Base<T>::mytype;
static base_type func() { return 0;}
};
this may seem like a silly issue, but the number of times the base class's mytype is used in the derived classes makes it pretty hideous in the former case. Is there a right way to get the types out of the templated base that maintains readability?
This is a well known quirk of the language for which there is no real solution. Lookup in templates is done in two separate steps, during the first phase of lookup before instantiation non-dependent names are resolved to their meanings, while in the second phase dependent names are resolved after instantiation.
The division of the two phases was included in the language to provide some sanity for the template developer that does not know where the template will be instantiated. The first phase of lookup is done at the point where the template is defined, and it can be reasoned about by the developer at exactly that point. At one point or another, the template will do operations that depend on the arguments, and those cannot be resolved where the template is defined, since the template arguments are not yet fixed. Those names are considered dependent, and lookup is postponed to the second phase, after substitution of the template arguments, so that ADL can kick in.
How does this relate to your particular problem? When you inherit from a non-template base, the developer has fixed what the base is, and that can be looked up at the point of the template definition as you expect. But when the base depends on a template argument, the definition of the base class is not known at the place of the derived template definition. In particular, without substituting the type in, the compiler cannot possibly know whether there is a specialization for this particular type. This implies that during the first phase the compiler cannot assume anything at all about the base and thus lookup cannot search into it.
The direct way of using a typedef in a base is for the developer of the derived template to explicitly tell the compiler that it wants a type and that the type will be defined in the instantiation of the base template. The key point here is that the compiler knows nothing about the base, but the developer can require that uses of this template comply with a contract in which the instantiation of the base must have that nested type. The developer is free to add constraints on the types of her template, the compiler is not.
The syntax for that is the one in your first block: typename Base<T>::type to refer to the type, which tells the compiler that the contract of use requires that whatever T is used to instantiate Derived, the user is required to ensure that Base<T> will contain a nested member type that is a type (typename). As a short hand, you can opt for the second approach: create a local typedef that will be found inside Derived and resolved to that. In that case, regular lookup during the first phase will find the nested typedef, determine that it refers to a dependent name and postpone the complete lookup for the second phase.
While this is not an answer to the question of whether there is a better way (readability wise), I hope it provides some understanding as of why things are like they are. The decision while designing the language was not arbitrary [it might or not be ideal, some people believe the first phase is unneeded and unwanted, but it was not arbitrary]
Maybe I'm missing something obvious here, but you can simply use using on the type directly without having to give it a new name:
template <typename T>
class Derived : public Base<T>{
public:
using typename Base<T>::mytype;
static mytype func() { return 0;}
};
You can even decide if the using-declaration for mytype should go into the public, protected or private section.

Must an unused function in a template class be valid?

In a template I have some functions which are only valid for certain template types. GCC seems to be happy with this, but I'm not sure it is valid. Unlike typical SFINAE the function itself is not a template.
template<typename T>
struct generic {
T item;
void get_limited() {
item.limited();
}
};
Provided I don't call get_limited, is it okay to instantiate this class with a type that does not implement limited?
If no, how can I solve this? I have a generic container class where certain features are enabled based on the allocate it is storing (so not directly on the type as above, but still a template parameter).
Template functions are instantiated on demand, so if there is no use of the function it need not be correct, at least for some possible instantiations. The standard does state that if a template is not valid for any instantiating type, the program is ill-formed (although the compiler is not required to diagnose it) even if it is never instantiated.
This feature is used in the standard library in different parts, where the requirements of a single function might be stricter than the general requirements that the template places on the instantiating types. For example, in the case of std::map, in general, the value type does not need to be default constructible, but if you want to use operator[] then it needs to be, since that operator might need to create an empty element if the key is not present.