It's possible to use an alias to change the literal signature of a function:
using String = std::string;
void Print(String s) { ... };
But this doesn't disallow calling Print with a std::string:
Print(std::string{"Hello world"}); // Still works
This makes sense -- an alias is strictly for simplifying a name for a type, it does not define a new type.
Besides subclassing, which is not a good idea, is there a mechanism by which it's possible to achieve strict typing by name for function parameters? An immediate consequence of this is that this would be possible as well:
using StringA = std::string;
using StringB = std::string;
void Print(StringA s) { ... };
void Print(StringB s) { ... };
Print(StringA{"Hello world"});
Print(StringB{"Hi everyone"});
My current solution is to define simple wrapper classes that hold the type I want to alias as a member. This isn't ideal because it requires duplicating the interface for the member class into the wrapper, something that isn't necessary when using an alias.
You are taking the right approach. Wrapping an object of the original type is the best that you can currently do.
I recently talked about this topic at CppCon 2015, and have open-sourced a library that makes it convenient to make an "opaque typedef" for integer types:
https://sourceforge.net/projects/opaque-typedef/
Be aware that it can be beneficial to customize the interface of your new type, to remove operations that would be errors for your purposes, and to add deliberate interoperability with other types. Slides from my presentation.
The standard makes typedefs and alias a synonym for another type and not a new type:
7.1.3/1: A name declared with the typedef specifier becomes a typedef-name. Within the scope of its declaration, a typedef-name is
syntactically equivalent to a keyword and names the type associated
with the identifier in the way described in Clause 8. A typedef-name
is thus a synonym for another type. A typedef-name does not introduce
a new type the way a class declaration or enum declaration does.
and
7.1.3/2: A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a
typedef-name. It has the same semantics as if it were introduced by
the typedef specifier. In particular, it does not define a new type and
it shall not appear in the type-id.
So unfortunately, you'll have to continue to use your class wrapper to introduce different types and being able to overload function on this base.
No, there is no way to get a new type without actually creating a new type (class, struct, enum).
However, there is a brand new proposal to add such a mechanism, perhaps using newtype as a keyword instead of typedef.
Related
As for as I know, currently in c++ there is no support for this.
For example,
class C{
struct{
enum {defaulted, opt1, opt2, ...} flag1;
enum {defaulted, optA, optB, ...} flag2;
} flags;
...
};
Now suppose we have C obj, to use the flag one would do
obj.flags.flag1 = obj.flags.opt1;
which is unnecessarily verbose.
However as the type of obj.flags.flag1 is known, in theory the compiler could lookup the name opt1 in that scope and save some typing.
Because the flags are not used anywhere else, it is preferred not to give names to the types (in fact it is difficult to come up with appropriate names). using enum syntax in c++20 does not solve this, because: it 1) requires the enum types to be named; 2) using multiple enums may lead to name collision, as shown here, since both enums have a defaulted member.
What will be the difficulties to implement this? Has this been proposed to the C++ standard committee and/or implemented in some compiler?
There could easily be an object named opt1 that could be converted to the appropriate enumeration type. It's better for name lookup to behave consistently rather than make special cases based on the types of other operands (partly because types are usually determined after name lookup).
In the following example
template <typename T>
void foo() {
const char* name = typeid(T).name();
std::cout << name;
}
the variable 'name' will be initialized by type 'T' name. It is very convinient if we need to print this template type name. But, if we have the alias:
using shortTypeName = std::smth::smth_else::smth_more;
in the result of the call
foo<shortTypeName>();
will be printed 'std::smth::smth_else::smth_more'. And I need to print exactly the alias name, but not the type it is defined. Can somebody give an advice, how I can do this?
... alias identificator ...
There's no such thing, at least not after compilation. It's just syntactic sugar and doesn't exist in any sense in the final executable.
The only way to grab the local name of a type (as opposed to the implementation-defined and probably-mangled typeid name) is with a stringize/stringify macro.
For future reference, this should eventually be possible when the Reflection TS lands - but I don't yet know whether to expect that to look like reflexpr(T).get_name(), or std::meta::name_of(^T), or something else again.
Can somebody give an advice, how I can do this?
The language does not support a mechanism to do this.
What you have is simply a type alias.
From http://en.cppreference.com/w/cpp/language/type_alias:
A type alias declaration introduces a name which can be used as a synonym for the type denoted by type-id. It does not introduce a new type and it cannot change the meaning of an existing type name.
You can't. Because a type alias is transparent: It is just a synonym for a new type, not a new type. As an implementation detail it doesn't even get mangled in the type system because it's not a type.
§7.1.3 The typedef specifier [dcl.typedef]
[...] A name declared with the typedef specifier becomes a typedef-name . Within the scope of its declaration, a typedef-name is
syntactically equivalent to a keyword and names the type associated
with the identifier in the way described in Clause 8. A typedef-name
is thus a synonym for another type. A typedef-name does not
introduce a new type the way a class declaration (9.1) or enum
declaration does.
A typedef-name can also be introduced by an alias-declaration . The identifier following the using keyword becomes a typedef-name and the
optional attribute-specifier-seq following the identifier appertains
to that typedef-name . It has the same semantics as if it were
introduced by the typedef specifier. In particular, it does not
define a new type and it shall not appear in the type-id .
typeid(T).name(); is pretty much useless anyway. Until we have proper introspection in C++ you have to resort to hacks to get what you want (macros, intrusive techniques or external code generator tools).
Clearly, type aliases and templated type aliases are semantically equivalent to typedefs and an extension of typedefs to support template. How come new syntax with the using keyword was created for these instead of using typedefs for the first and some syntax extension with the word typedef.
NOTE: This is not a clone of the "difference between using and typedef" question. I know that using gives the advantage of defining a family of typedefs. What I am asking is why did the standard people decide on having this extension use the using keyword instead of the typedef keyword. This seems like it just adds confusion in the language.
Here is what Bjarne Stroustrup says about why they introduced using instead of extending typedef:
The keyword using is used to get a linear notation "name followed by
what it refers to." We tried with the conventional and convoluted
typedef solution, but never managed to get a complete and coherent
solution until we settled on a less obscure syntax.
He also claims that he likes this syntax also more for usual typedefs:
In addition to being important in connection with templates, type
aliases can also be used as a different (and IMO better) syntax for
ordinary type aliases:
using PF = void (*)(double);
He is quite correct here, this seems very clean. In contrast a typedef would be extremely convoluted with the name being somewhere in the middle:
typedef void(*PF)(double);
Here is an explanation (see page 4) from their proposal that is even more in-depth:
It has been suggested to (re)use the keyword typedef — as done in the
paper [4] — to introduce template aliases:
template<class T>
typedef std::vector<T,MyAllocator<T>> Vec;
That notation has the advantage of using a keyword already known to introduce a type alias. However, it also displays several disadvantages among which the confusion of using a keyword known to introduce an alias for a type-name in a context where the alias does not designate a type, but a template; Vec is not an alias for a type, and should not be taken for a typedef-name. The name Vec is a name for the family std::vector<*,MyAllocator<*>> - where the asterisk is a placeholder for a type-name.
Consequently, we do not propose the "typedef" syntax.
template<class T>
using Vec = std::vector<T,MyAllocator<T>>;
can be read/interpreted as: from now on, I'll be using Vec<T> as a synonym for std::vector<T,MyAllocator<T>>. With that reading, the new syntax for aliasing seems reasonably logical.
So he has basically two points here:
A using template becomes a family of types, not a type, so typedef is "wrong"
using can be read almost as an english sentence
What you suggest was actually proposed back in 2002 in document N1406 by Herb Sutter. It would allow, for example, to write:
template<typename T> typedef X<T,int> Xi;
This was later revised in N1449 by Gabriel Dos Reis and Mat Marcus. They adopt the using syntax, and note the following:
Note that we specifically avoid the term “typedef template” and introduce the new syntax involving the pair “using” and “=” to help avoid confusion: we are not defining any types here, we are introducing a synonym (i.e. alias) for an abstraction of a type-id (i.e. type expression) involving template parameters.
They also state:
Two straw polls were taken regarding syntax. A strong majority voted to avoid the typedef template syntax, in favor of the “=” syntax. A second vote indicated strong preference for the “using” keyword as opposed to a word like “alias” or the absence of any keyword as in the draft version of this proposal. The motivation for using any keyword at all stemmed partly from the desire to use a syntax that might be compatible with the non-template aliasing direction briefly outlined above.
This syntax was then adopted in the final proposal N2258 by Gabriel Dos Reis and Bjarne Stroustrup.
I've been looking high and low for an answer to what I thought was a fairly simple question: Why are access declarations deprecated?
class A
{
public:
int testInt;
}
class B: public A
{
private:
A::testInt;
}
I understand that it can be fixed by simply plopping "using" in front of A::testInt,
but without some sort of understanding as to why I must do so, that feels like a cheap fix.
Worse yet, it muddies my understanding of using declarations/directives, and the scope resolution operator. If I must use a using declaration here, why am I able to use the SRO and only the SRO elsewhere? A trivial example is std::cout. Why not use using std::cout? I used to think that using and the SRO were more or less interchangeable (give or take some handy functionality provided with the "using" keyword, of which I am aware, at least in the case of namespaces).
I've seen the following in the standard:
The access of a member of a base class can be changed in the derived class by mentioning >its qualified-id in the derived class declaration. Such mention is called an access >declaration. The effect of an access declaration qualified-id; is defined to be equivalent >to the declaration using qualified-id; [Footnote: Access declarations are deprecated; member >using-declarations (7.3.3) provide a better means of doing the same things. In earlier >versions of the C++ language, access declarations were more limited; they were generalized >and made equivalent to using-declarations - end footnote]
However, that really does nothing other than confirm what I already know. If you really boiled it down, I am sure my problem stems from the fact that I think using and the SRO are interchangeable, but I haven't seen anything that would suggest otherwise.
Thanks in advance!
If I must use a using declaration here, why am I able to use the SRO and only the SRO elsewhere?
Huh? You are not able to. Not to re-declare a name in a different scope (which is what an access declaration does).
A trivial example is std::cout. Why not use using std::cout?
Because they're not the same thing, not even close.
One refers to a name, the other re-declares a name.
I am sure my problem stems from the fact that I think using and the SRO are interchangeable
I agree that's your problem, because you are entirely wrong. Following a using declaration it is not necessary to qualify the name, but that doesn't make them interchangeable.
std::cout is an expression, it refers to the variable so you can write to it, pass it as a function argument, take its address etc.
using std::cout; is a declaration. It makes the name cout available in the current scope, as an alias for the name std::cout.
std::cout << "This is an expression involving std::cout\n";
using std::cout; // re-declaration of `cout` in current scope
If you're suggesting that for consistency you should do this to write to cout:
using std::cout << "This is madness.\n";
then, erm, that's madness.
In a class, when you want to re-declare a member with a different access you are re-declaring it, so you want a declaration. You aren't trying to refer to the object to write to involve it in some expression, which (if it was allowed at class scope) would look like this:
class B: public A
{
private:
A::testInt + 1;
};
For consistency with the rest of the language, re-declaring a name from a base class is done with a using-declaration, because that's a declaration, it's not done with something that looks like an expression.
class B: public A
{
private:
A::testInt; // looks like an expression involving A::testInt, but isn't
using A::testInt; // re-declaration of `testInt` in current scope
};
Compare this to the std::cout example above and you'll see that requiring using is entirely consistent, and removing access declarations from C++ makes the language more consistent.
The following code yields an error error: ‘struct Foo’ is not a valid type for a template constant parameter:
template <struct Foo>
struct Bar {
};
Why is that so?
template <class Foo>
struct Bar {
};
works perfectly fine and even accepts an struct as argument.
This is just an artifact of the syntax rules - the syntax just lets you use the class or typename keywords to indicate a type template parameter. Otherwise the parameter has to be a 'non-type' template parameter (basically an integral, pointer or reference type).
I suppose Stroustrup (and whoever else he might have taken input from) decided that there was no need to include struct as a a keyword to indicate a type template parameter since there was no need for backwards compatibility with C.
In fact, my recollection (I'll have to do some book readin' when I get back home) is that when typename was added to indicate a template type parameter, Stroustrup would have liked to take away using the class keyword for that purpose (since it was confusing), but there was too much code that relied on it.
Edit:
Turns out the story is more like (from a blog entry by Stan Lippman):
The reason for the two keywords is
historical. In the original template
specification, Stroustrup reused the
existing class keyword to specify a
type parameter rather than introduce a
new keyword that might of course break
existing programs. It wasn't that a
new keyword wasn't considered -- just
that it wasn't considered necessary
given its potential disruption. And up
until the ISO-C++ standard, this was
the only way to declare a type
parameter.
Reuses of existing keywords seems to
always sow confusion. What we found is
that beginners were [wondering]
whether the use of the class
constrained or limited the type
arguments a user could specify to be
class types rather than, say, a
built-in or pointer type. So, there
was some feeling that not having
introduced a new keyword was a
mistake.
During standardization, certain
constructs were discovered within a
template definition that resolved to
expressions although they were meant
to indicate declarations
...
The committee decided that a new
keyword was just the ticket to get the
compiler off its unfortunate obsession
with expressions. The new keyword was
the self-describing typename.
...
Since the keyword was on the payroll,
heck, why not fix the confusion caused
by the original decision to reuse the
class keyword. Of course, given the
extensive body of existing code and
books and articles and talks and
postings using the class keyword, they
chose to also retain support for that
use of the keyword as well. So that's
why you have both.
You can instantiate a template using a struct; however, the syntax for declaring a template type only allows the keywords "class" or "typename" to appear where you are attempting to use the keyword "struct".
I should add that you can also use a specific type (e.g. int), if you want to instantiate your template based on a compile-time constant or based on an object with external linkage... but that's somewhat of an aside.
The short answer is: template <class Foo> even accepts a union or a double - still, neither is allowed instead of class. However, typename is. That's just the way the syntax was defined.
A somewhat longer answer: When templates for C++ where "invented", there was a keyword needed at that place saying that the next identifier would be a type name. It was decided to re-use the existing class keyword. That was a bit confusing, but there's a general reluctance to introducing more keywords, because they always break some existing code which used this as an identifier when it wasn't a keyword.
Later, typename became a keyword for other reasons, and since it is a much better fit, it can now be used in that place: template <typename Foo>. However, with billions of lines of code out there using class in that place, it must remain valid for that purpose. So now both are allowed.
As is common in C++, this created several camps as to what keyword to use in that place. Some stick with class, because they've been using it for more than a decade. Others prefer typename, because it's a much better fit. Some use class when Foo is expected to be of a class type (members are accessed) and typename when built-ins can be used, too.
Because the keyword for template parameters is class or typename. This doesn't restrict the Foo parameter to be a class - it can be of any type.