I'm working on a project where SFINAE is used to detect whether a member function exists. I'm running into a problem with the following example:
class Base
{
private:
template <class T>
void foo( T t ) {}
void snarf() {}
};
class Derived : public Base
{
public:
template <class T>
void bar( T t )
{
foo( t ); // shouldn't be possible
snarf(); // bug in gcc, correctly identified as error in clang
}
};
The problem seems to be that the access control (e.g. private) in the base class is not being respected in the derived class when trying to access inherited functions. Both foo and snarf should be inaccessible from the derived class. So when I try to test if some derived class has some function, it is incorrectly accessing the base class to find a match.
In g++ 4.8.1, both calling foo and snarf are incorrectly allowed to happen, which I'm pretty sure is an instance of this bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41437
In clang++ 3.3, calling snarf is correctly identified as an error, but foo is allowed to be called, which seems like it should also be an error. Does this look like a bug in clang++? (I've posted a bug report just in case it turns out to be http://llvm.org/bugs/show_bug.cgi?id=16410)
Given that this may well be a bug in both compilers, is there any clever workaround I can use to detect that derived doesn't really have access to foo? The way I'm actually using this is the following:
template <class T, class T2, class Unused = void>
struct has_member_foo : std::false_type {};
Where has_member_foo is specialized to extend from std::true_type when calling declval<T&>().foo(declval<T2&>()) resolves to a valid function ala Checking a member exists, possibly in a base class, C++11 version. I need this to fail when a derived class inherits a private version of foo.
What does the compiler say when you write code that attempts to call bar?
Derived d;
d.bar<int>(42);
If it displays an error there, then I think things are working as expected. Until you actually write code which uses a template method, there is nothing to compile, so you may not see any errors coming from it if you are using a less intelligent compiler.
You could even define bar like this:
template <class T>
void bar( T t )
{
foo( t );
snarf();
snorf();
divideByZero();
asdf--+|&*
}
And it will still compile (with some compilers), as long as you never try to use bar.
Related
I am a beginner in STL. I'm trying to code a toystl to learn STL. When I code about iterator, I'm puzzled if I should code a simple auto_ptr first and inherint from it.
I wrote a base class called iterator. And now it works like this,
struct iterator{};
template <class T>
struct vector_itorater: public toystl::iterator<toystl::random_access_iterator_tag, T>{};
If i need another base class works like a "auto_ptr"? just like this
// firstly define a sort of auto_ptr as base class
struct auto_ptr{};
// secondly inherint from auto_ptr
template <class T>
struct vector_itorater: public auto_ptr{};
Does this work? Or does STL do it like this?
I think you mixed up runtime polymorphy and compile time polymorphy. When the compiler instantiates a template, it cares about its visible interface of the concrete object. It does not care if this object has a inheritance relationship with other classes, it will pass as long as the concrete object can be used within the concrete context.
template <class C>
void foo(const C& bar)
{
// at the time of writing we don't know anything of C,
// only that it has a callable baz member (either a
// member function or a member with a call operator).
// This works, since the compiler knows the exact type
// during template instantiation, but we don't have to
// care in advance.
bar.baz();
}
struct X
{
void baz() const;
};
void grml()
{
X x;
// The compiler fills in X as the template type
// parameter for us. So the compiler creates a
// void foo<X>(const X&) function for us.
foo(x);
}
In this example when the compiler sees the template, it has no clue how this template will be called later. Only once the template gets instantiated (used), the compiler then will check if the passed type is suitable for this template.
Here it is not needed to have a common base class to derive every possible implementation from. The STL uses templates in order to avoid to use such base classes, since they give you a burden on your design later, and if you have virtual members in the base to override, you can get a serious performance penalty.
EDIT: just to clarify, I understand why this code does not work,and I'm not attempting to fix it, but to understand what are the dangers if this code could compile without the semantic error.
I have found that the following code will cause a static semantic error.
I know that it's because std::list is a template class and that means that std::list<foo*> is a different type than std::list<bar*>
My question is, if the compiler would allow this type of conversion from std::list<bar*> to std::list<foo*> in the second call to print_all , what could be the danger?
I have searched around the site but couldn't find examples to bad things that could happen.
I have also tried to think of such examples but I'm not sure that these are correct or accurate.
(for example if the compiler would allow this, could I add a foo* object into the bar list in print_all() because it was converted to list?).
#include <list>
using std::list;
class foo {
// ...
};
class bar : public foo {
// ...
};
static void print_all(list<foo*>& L) {
// ...
}
list<foo*> LF;
list<bar*> LB;
// ...
print_all(LF); // works fine
print_all(LB); // static semantic error
list<foo*> and list<bar*> are two completely different classes, with no connection between them, other than beeing generated from the same template std::list (template<class T, class Allocator = std::allocator<T>> class list;). As such there isn't a conversion between them, unless the author of the template std::list explicitly wrote a conversion between std::list<T, A> and std::list<U, A> when T is a base class of U. And that is not written.
One way to achieve what you want is by creating template function:
template <class T, class A>
void print_all(std::list<T*, A>& l)
Now, there are a few caveats to be aware of:
I see no reason why you would make that static member. I would make it free function, but I would put it in a namespace with foo and bar
If you want to restrict it's usage strictly to foo and its derived classes you can do it with sfinae:
template <class T, class A, class E = std::enable_if_t<std::is_base_of_v<foo, T>>>
auto print_all(std::list<T*, A>& l)
{
}
And finally you should consider turning this into idiomatic C++ way of printing, aka streams and add the operator<<(std::ostream&, const std::list<T*, A>&) along with operator<<(std::ostream, const foo&) and maybe a virtual print function on foo. Here is extra important to define them in the same namespace as foo and bar.
You cannot use one vector instead of other because are different classes, but you can convert one vector to other implicitely or explicitely, using transformation or copy/ move constructors and assignment operator. It can cost you additional performance ( and memory in case of copy) overhead, but can be a deal in your particular case.
For example, I got a class looks like this:
template <typename T>
class Test {
public:
T* t;
...
};
then (how) can I extends the class which is similar like this?
class TestImplement : Test<TestImplement::TestInner> // the TestInner class is defind in the class {
class TestInner {};
...
};
Since the TestInner is defined after where the template needs, this code is illegal, and a forward declaration is useless too.
Well, you simply can't do that, and you've already pointed out why that is.
Thinking more about logic than about syntax, since the base class definition may very well differ depending on what its template argument is, and since the base class definition may very well affect the way TestInner works, you are attempting to create a circular dependency which is just not possible in our physical reality.
You could experiment with using TestImplement as the template argument (CRTP!) and then look up typename TemplateArgument::TestInner from within the base, but even if this works I doubt its clarity.
I'd use a non-nested class instead.
I have a templated class where I ensure the type parameter is a subclass of some abstract base class like this:
#include <type_traits>
template<typename T>
class Foo
{
static_assert(
std::is_base_of<MyBase, T>::value,
"T must be a descendant of MyBase"
);
void SomeMethod()
{
auto bar = T();
//or
auto bar = T("Constructor with parameter")
bar.someFunctionOnMyBase();
}
};
Now in programming languages like C# or Java I can use this information and use he type information to call methods on the template type. Is such thing possible? Bonus points if it also possible to call constructors with the correct parameters.
Yes, this is totally fine (as long as both the constructor that you intend to call and the destructor of class T are publicly accessible).
As a matter of fact you don't even need to have the static_assert, and as long as a member function T::someFunctionOfMyBase exists (even if it isn't the one defined in MyBase and just happens to share the name), this will still compile.
I have the following class:
template <class T>
class BeliefSet : public Belief<T>
{
private:
std::vector<T> m_Facts;
public:
void RemoveFact(const T Fact)
{
m_Facts.remove(Fact);
}
};
This works fine. However, I want to derive another class from BeliefSet and override this method RemoveFact(), thus I changed the code shown above to the following:
/* Rest of this class has not been changed. */
virtual void RemoveFact(const T Fact)
{
m_Facts.remove(Fact);
}
Now as soon as I compile I get this error:
error C2039: 'remove': is not a member of 'std::vector<std::string,std::allocator<_Ty>>'
Any ideas what I am doing wrong here?
This works fine.
No, it doesn't. std::vector has no member function remove(). However, class template member functions aren't eagerly instantiated. It's likely that you simply never invoked RemoveFact(), so you never had to run into this problem. This "lazy" instantiation is very important - it lets you write and use class templates that have conditionally valid operators without writing loads of SFINAE junk (e.g. I can use std::map fine with non-default-constructive value types, I just can't use operator[]).
When you made the function virtual, as you as inherit from it, it is likely that your compiler attempted to instantiate the function at that point (it is unspecified whether an implementation does so - yours apparently does). Since this function is ill-formed, you get the error via the virtual function instantiation instead of normal function instantiation.
Either way, the function is broken and you want:
void RemoveFact(const T& Fact)
{
m_Facts.erase(
std::remove(m_Facts.begin(), m_Facts.end(), Fact),
m_Facts.end());
}