Monday, 26 December 2016

c++11 - Trouble understanding C++ dependent types, vs what's on the current instantiation

The code below is adapted from the answer here: https://stackoverflow.com/a/17579889/352552




My purpose in asking this question is try to to understand better how C++ handles type resolution around dependent types, versus what's considered to be on the current instantiation, and therefore not needing a typename qualifier. I've been getting contradictory results from different compilers, so I've come here looking for a more canonical answer.



Consider this code



#include 

struct B {
typedef int result_type;
};


template
struct C {
};

template<>
struct C {
typedef float result_type;
};

template

struct D : B, C {
std::string show() {
//A) Default to current instantiation - ignore dependent type, even if one exists, or so I hope
D::result_type r1;

//B) What **exactly** does typename add, here?
//typename D::result_type r1;

return whichType(r1);
}


std::string whichType (int val){
return "INT";
}
std::string whichType (float val){
return "FLOAT";
}
};



int main() {
D stringD;
D floatD;
std::cout<<"String initialization "< std::cout<<"Float initialization "<}


line A) in show(), if I understand correctly, tells the compiler to use the current instantiation, so I should get INT INT. On GCC, I do. So far, so good.




Line B, again if I understand correctly, should either tell the compiler to consider dependent types, which would make that line error out because of the ambiguity; or, if that means only consider dependent types, I should get INT FLOAT. On GCC I get INT INT there, too. Why?






Running this on Clang.



Line A doesn't compile at all.




error: no type named 'result_type' in 'D'; did you mean simply 'result_type'? D::result_type r1;





dropping the D:: does indeed yield INT INT.



Should it have compiled, or is Clang correct here?



Line B does indeed error on the ambiguity




error: member 'result_type' found in multiple base classes of different types typename D::result_type r1








Can anyone here say with authority which compiler (if any!) is canonically correct, and why?



Assuming Clang is correct, it might imply that



MyType::F




is invalid for referencing a type from the current instantiation if it exists on a base type; it's only valid if the type is defined on that class. Ie adding



typedef double dd;



to D



and then



D::dd d = 1.1;
std::cout<


in show would work just fine, which is indeed the case.



Moreover,



typename D::sometype



seems to mean consider dependent types, but not exclusively, and so expect errors if such a type winds up defined in multiple places, either in the current instantiation, and or dependent on a template parameter.




But again, this all assumes Clang's behavior is correct according to spec, which I can't speak to.






Link to GCC repl I was using:
https://wandbox.org/



Link to Clang repl I was using:
https://repl.it/languages/cpp11

No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...