0

I believe using keyword was added in order to allow template typdefs. However I came across to one compilation error, here is simplified code:

template <bool EnableFirst, class T1, class T2>
struct OneOfTwo {};
template <class T1, class T2>
struct OneOfTwo<true, T1, T2>
{
    using type = T1;
};
template <class T1, class T2>
struct OneOfTwo<false, T1, T2>
{
    using type = T2;
};

struct A
{
    static constexpr int X = 1;
};

struct B
{
    static constexpr int X = 12;
};

struct C {};
struct D {};

template <class T1, class T2>
using ClassX = OneOfTwo<T1::X == T2::X, C, D>::type;            //C4346: 'T2::X': dependent name is not a type;  C2061: syntax error: identifier 'type'

template <class T1, class T2>
struct ClassY : public OneOfTwo<T1::X == T2::X, C, D>::type {}; //OK

And I use these classes like this:

ClassY<A, B> y;
ClassX<A, B> x;

I believe ClassX and ClassY should be exactly same thing, however ClassX causes compilation error. So my question is: which part of C++ standard did I break?

BTW, I'm using MSVC 2015 toolset with XP support (v140_xp).

ST3
  • 8,826
  • 3
  • 68
  • 92

2 Answers2

1

You are missing a typename keyword:

template <class T1, class T2>
using ClassX = typename OneOfTwo<T1::X == T2::X, C, D>::type;
Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
0

The using keyword requires types in its definition:

using ABC = SomeClass;

But ABC is not a type, it is a type alias (a.k.a typename)...

So, this line fails:

using ClassX = OneOfTwo<T1::X == T2::X, C, D>::type;

C4346: 'T2::X': dependent name is not a type; C2061: syntax error: identifier 'type'

A more accurate representation of this error would be:

Error: typename is not class...

typename is a specifier for type-aliases which can be anything, from ints to void*s to anything... But a class has its own identity... it's a class...

So, in order to fix your error you have to specify typename before a type alias...

// Adding the 'typename' keyword is also needed for nested classes, according to YSC
using ClassX = typename OneOfTwo<T1::X == T2::X, C, D>::type;

Read: Type aliases


Edit: Doing something like this will also yield the same thing:

template <class T1, class T2>
struct OneOfTwo<false, T1, T2>
{
    typedef T2 type;
}

In typedef though, you cannot make an alias of a type alias, just like you do with using, so using has an advantage...

Note: As suggested: adding 'typename' is considered redundant since C++20...

Ruks
  • 3,886
  • 1
  • 10
  • 22
  • `So, in order to fix your error you have to specify typename before a type alias...` Note, that it's no longer necessary starting from C++20. Now, `typename` is automatically assumed/inferred in the context there the is no ambiguity (like using declaration since it can only contain type). See [P0634R3](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html) (currently only implemented in GCC 9). – Dan M. Dec 28 '18 at 12:00
  • @DanM. Note that Microsoft's compiler still doesn't support *C++20* and looks like the OP is using the Microsoft compiler anyways... It might be good if they soon implemented it... – Ruks Dec 28 '18 at 12:02
  • true for VS2015, but I'm just noting that in case someone encounters the same error on a newer compiler in the future (since since VS2017 it uses standard switches too and C++20 won't become the default for a long while but might be still available as a switch). – Dan M. Dec 28 '18 at 12:07
  • 1
    I'm not a fan of your explanation. `typename` is not needed _because_ `type` is a type-alias; it would equally be needed if it was a nested class. [demo](http://coliru.stacked-crooked.com/a/06fd6c8cde64a4f5) – YSC Dec 28 '18 at 12:42