The code below compiles correctly with g++ 4.8.1 (mingw), and a variety of recent clang and gcc versions on http://gcc.godbolt.org/, however with MSVC2013 Update 4 it fails, apparently due to the typedef typename A<T>::value_type value_type; line. The compiler gives the following error:
x.cpp(30): error C2893: Failed to specialize function template '
void B<C,int>::bar(void)' With the following template arguments: 'MemberFn=void C::baz(int)'
The simpler typedef typedef T value_type; works.
Am I doing something wrong? Or is this a known bug in the Microsoft C++ compiler?
Supplementary question:
typedef T value_type;) or to pull them in from the base class (e.g. typedef typename A<T>::value_type value_type;, or in C++11 using typename A<T>::value_type;)? (Note: I'm hedging on C++03 compatibility right now, hence the avoidance of using.) There was some inconclusive discussion here: Use typedef/using from templated base class in derived class The reason I ask is that if typedef T value_type; is preferred anyway, I don't need to worry so much.#include <cstdio>
template <typename T>
struct A {
typedef A<T> base_type;
typedef T value_type;
};
template <typename Derived, typename T>
struct B : public A<T> {
typedef Derived derived_type;
//typedef T value_type; // this works
typedef typename A<T>::value_type value_type; // this fails in MSVC 2013
//using typename A<T>::value_type; // this fails in MSVC 2013 too
template<void (derived_type::*MemberFn)(value_type) >
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
struct C : public B<C, int> {
void baz(int i)
{
std::printf("baz(%d)\n", i);
}
void foo()
{
bar<&C::baz>();
}
};
int main(int, char *[])
{
C c;
c.foo();
}
Update #1: This is a reduced test case from a framework. I am not asking for general critique of the structure. I do not expect the structure to make sense without context.
Update #2: Here is a related question discussing whether using is valid in combination with typename: C++ template inheritance issue with base types
Update #3: I have filed a public bug report at Microsoft Connect. If you can reproduce the issue, and believe that it is a bug, please upvote the bug: https://connect.microsoft.com/VisualStudio/feedback/details/1740423
I have a possible solution (I can only test this with VS2015)
If you use the base type as a template parameter itself it is resolved and you can access the value_type.
template <typename T>
struct A {
typedef T value_type;
typedef A<T> base_type;
};
template <typename Derived, typename T, typename Base = A<T>>
struct B : public Base
{
typedef Derived derived_type;
typedef void (derived_type::*member_func_type)(typename Base::value_type);
template<member_func_type MemberFn>
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
struct C : public B<C, int>
{
void baz(int i)
{
std::printf("baz(%d)\n", i);
}
void foo()
{
bar<&C::baz>();
}
};
Or the solution I would prefer:
template <typename T>
struct A {
typedef A<T> base_type;
typedef T value_type;
};
template <typename Derived, typename T, typename Base = A<T>>
struct B : public Base
{
typedef Derived derived_type;
typedef typename Base::base_type base_type;
typedef typename Base::value_type value_type;
typedef void (derived_type::*member_func_type)(value_type);
template<member_func_type MemberFn>
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
I must admit that this solution has its flaws, like the ability to override the 3rd template parameter with something else. At least the other base class needs to have base_type and value_type declared.
EDIT:
Using a static_assert can prevent changing the Base template parameter.
template <typename Derived, typename T, typename Base = A<T> >
struct B : public Base
{
static_assert(std::is_same<Base, typename A<T>>::value, "Redefinition of template parameter Base is not allowed");
typedef Derived derived_type;
typedef typename Base::base_type base_type;
typedef typename Base::value_type value_type;
typedef void (derived_type::*member_func_type)(value_type);
template<member_func_type MemberFn>
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
Example:
template <typename T>
struct D {
typedef D<T> base_type;
typedef T value_type;
};
struct E : public B<C, int, D<int>>
{
};
Result:
error C2338: Redefinition of template parameter Base is not allowed
UPDATE:
Changing the order of the template parameters of B changes the behaviour. Here the original code with just the order of T and Derived changed.
#include <cstdio>
template <typename T>
struct A {
typedef A<T> base_type;
typedef T value_type;
};
template <typename T, typename Derived>
struct B : public A<T> {
typedef Derived derived_type;
//typedef T value_type; // this works
typedef typename A<T>::value_type value_type; // this fails in MSVC 2013
//using typename A<T>::value_type; // this fails in MSVC 2013 too
template<void (derived_type::*MemberFn)(value_type) >
void bar()
{
(static_cast<derived_type*>(this)->*MemberFn)(42);
}
};
struct C : public B<int, C> {
void baz(int i)
{
std::printf("baz(%d)\n", i);
}
void foo()
{
bar<&C::baz>();
}
};
int main(int, char *[])
{
C c;
c.foo();
}
This compiles and works fine.
I'm still not completely sure that this is a bug.
Update
So this seems to be a missing feature in MSVC. MSVC (up to 2015/14.0) doesn't seem to support "Two-phased-name-lookup".
Darran Rowe: VC hasn't implemented three C++98/03 features: two-phase name lookup, dynamic exception specifications, and export. Two-phase name lookup remains unimplemented in 2015, but it's on the compiler team's list of things to do, pending codebase modernization. Dynamic exception specifications also remain unimplemented (VC gives non-Standard semantics to throw() and ignores other forms), but they were deprecated in C++11 and nobody cares about them now that we have noexcept. It's unlikely that we'll ever implement them, and there's even been talk of removing them from C++17. Finally, export was removed in C++11.
Source C++11/14/17 Features In VS 2015 RTM
There has been a bug requesting that feature in 2012 but it was closed without a comment: support two-phase name lookup - by Ivan Sorokin
So it seems like you are doing everything right here, but MSVC just doesn't support this part of the C++ standard.
Looks right. I tested it with VS2015, same error message. In fact, it should work without the typedef in B because it inherits the 'value_type' from A, so it's also in the namespace if struct B.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With