dynamic cast a reference and auto - c++

I've encountered a pretty weird behavior when using auto and dynamic_cast.
This is the class hierachy i have:
class BaseInterface {
public:
virtual void someMethod()=0;
};
class Derived:public BaseInterface {
public:
virtual void someMethod1()=0;
void someMethod()override;
};
And of course there are some classes that implement all derived methods.
Then there is a third class which looks like this:
class ThirdClass {
public:
void demoMethod(BaseInterface&);
void anotherMethod(Derived&);
};
void ThirdClass::demoMethod(BaseInterface& obj) {
auto buffer=dynamic_cast<Derived&>(obj);
anotherMethod(buffer);
}
When i compile this with gcc i get an "cannot allocate an object of abstract type" error. Whereas when i replace
auto buffer=...
with
Derived& buffer=...
everything compiles fine. Why is that the case? Is auto not deducing the right type or something?
Also i found a dirty trick to still use auto:
void ThirdClass::demoMethod(Base& obj) {
auto buffer=dynamic_cast<Derived*>(&obj);
anotherMethod(*buffer);
}

You're getting Derived from auto. Use this instead:
auto & buffer = dynamic_cast<Derived&>(obj);

§7.1.6.4/7:
When a variable declared using a placeholder type is initialized […]
the deduced return type or variable type is determined from the type
of its initializer. […] let T be the declared type of the variable
or return type of the function. If the placeholder is the auto
type-specifier, the deduced type is determined using the rules for
template argument deduction. […] obtain P from T by replacing the
occurrences of auto with either a new invented type template
parameter U[…]. Deduce a value for U using the rules of template
argument deduction from a function call (14.8.2.1), where P is a
function template parameter type and the corresponding argument is the
initializer.
So, in order to familiarize yourself with the process, take a look at the actual rule used for deducing the type of buffer: What happens if you change
template <typename U>
void f( U );
to
void f( Derived& );
when calling f with an lvalue of type Derived? Clearly, for the function template, U will be deduced as Derived, which then yields a deduction failure.
This directly corresponds to the deduction of the placeholder type in your example - auto will be replaced by Derived, and that fails, as Derived is abstract.
Generally speaking, if you write
auto obj = …;
obj will never be a reference, just as U will never be deduced as a reference type when calling the above function template.
Instead, use auto&:
auto& buffer = dynamic_cast<Derived&>(obj);
Now, P is U&:
template <typename U>
void f(U&);
U is, of course, still deduced as Derived, but the type of P - which is effectively the type of buffer - is Derived&.

Related

difference between using a template function type and std::function

I am curious as to why a2 is fine, but b2 won't compile:
#include <functional>
#include <iostream>
class A {
public:
A(std::function<void(int)>&& func) : f(std::move(func)) {}
std::function<void(int)> f;
};
template <class F>
class B {
public:
B(F&& func) : f(std::move(func)) {}
F f;
};
int main() {
int a = 1;
auto f = [a](int b){std::cout << a+b << '\n';};
A a1([a](int b){std::cout << a+b << '\n';});
A a2(f);
B b1([a](int b){std::cout << a+b << '\n';});
// B b2(f);
a1.f(2);
a2.f(2);
b1.f(2);
// b2.f(2);
f(2);
}
A(std::function<void(int)>&& func)
A can be initialized with std::function rvalue. Now, f (in main) is not a std::function, seeing as each lambda has its own distinct type. But we can create a temporary std::function out of it, and bind the rvalue reference func to that.
B(F&& func)
Don't let appearances fool you. This may look like a forwarding reference, but it isn't. Forwarding references are syntactically rvalue reference to a template parameter, but it must be a template parameter of the function we are forwarding into.
The constructor of B is not a template, and so func is not a forwarding reference.
The deduction guide that gets generated from that constructor accepts only rvalues, and deduces F from that. Because f (the local lambda in main) is an lvalue, it cannot bind to an rvalue reference and so CTAD cannot succeed. Indeed std::move(f) will make b2 well-formed.
If you want to accept lvalues for arguments as well, you may add another constructor
B(F const& func) : f(func) {}
Now there are two deduction guides being generated, one for each value category.
B b2(f);
B is not a class, or a type, it is a template. An object declaration in C++ is, basically a declaration: here's a type, and here's one or more objects of that type (or a derived type). A template is not a type. A template instantiation is a type.
std::vector b2;
This won't compile for the same reason your code will not compile. You have to specify the template parameter to instantiate a type, such as:
std::vector<int> b2;
The same reason explains your compilation error.
Having said that, a small change to your template will make it compile as long as your compiler supports C++17:
template <class F>
class B {
public:
B(F func) : f(std::move(func)) {}
F f;
};
A C++17 compiler will be able to deduce the template parameter due to class template deduction guides in C++17. And, it's possible that with a little bit of additional work (I haven't looked into it) it might be possible to fiddle a few things and make your original template also work in C++17.

Template argument deduction from inherited type

I want a setup like the following:
template <typename T> class a {};
class b : public a<int> {};
template <typename T>
void do_foo(std::unique_ptr<a<T>> foo)
{
// Do something with foo
}
int main()
{
do_foo(std::make_unique<b>());
}
This fails to compile with a note saying template argument deduction/substitution failed and mismatched types 'a<T>' and 'b'. It's pretty self-explanatory. I can help the compiler along by writing do_foo<int>(std::make_unique<b>());, but then I'm repeating myself by writing int twice.
Is there a way to get the compiler to deduce the template parameter in this case? And what would you call this behaviour? I tried searching for things like "template type deduction for inherited type", "polymorphic template deduction" etc.
Is there a way to get the compiler to deduce the template parameter in this case?
No. Not in C++14 (or even C++20).
And what would you call this behaviour?
Standard compliant. To be specific, this paragraph applies:
[temp.deduct.call]
4 In general, the deduction process attempts to find template
argument values that will make the deduced A identical to A (after
the type A is transformed as described above). However, there are
three cases that allow a difference:
If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than the
transformed A.
The transformed A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification
conversion ([conv.qual]).
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A.
Likewise, if P is a pointer to a class of the form
simple-template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A.
This is an exhaustive list of cases where a template argument can be validly deduced from a function argument even if it doesn't match the pattern of the function parameter exactly. The first and second bullets deal with things like
template<class A1> void func(A1&){}
template<class A2> void func(A2*){}
int main() {
const int i = 1;
func(i); // A1 = const int
func(&i); // A2 = const int
}
The third bullet is the one that is closest to our case. A class derived from a template specialization can be used to deduce a template parameter pertaining to its base. Why doesn't it work in your case? Because the function template parameter is unique_ptr<a<T>> and the argument you call it with is unique_ptr<b>. The unique_ptr specializations are not themselves related by inheritance. So they don't match the bullet, and deduction fails.
But it doesn't mean that a wrapper like unique_ptr prevents template argument deduction entirely. For instance:
template <typename> struct A {};
struct B : A<int> {};
template<typename> struct wrapper{};
template<> struct wrapper<B> : wrapper<A<int>> {};
template<typename T>
void do_smth(wrapper<A<T>>) {}
int main() {
do_smth(wrapper<B>{});
}
In this case, wrapper<B> derives from wrapper<A<int>>. So the third bullet is applicable. And by the complex (and recursive) process of template argument deduction, it allows B to match A<T> and deduce T = int.
TL;DR: unique_ptr<T> specializations cannot replicate the behavior of raw pointers. They don't inherit from the specializations of unique_ptr over T's bases. Maybe if reflection ever comes to C++, we'll be able to meta-program a smart pointer that does behave that way.
As workaround, you might add overload:
template <typename T>
void do_foo_impl(a<T>* foo)
{
return do_foo(std::unique_ptr<a<T>>(foo));
}
template <typename T>
auto do_foo(std::unique_ptr<T> foo) -> decltype(do_foo_impl(foo.release()))
{
do_foo_impl(foo.release());
}
Demo

Pass a reference to a base class as template parameter

I have the following code where I try to specialize a function template for each instance of a class (that may have been derived):
class Base {
};
class Derived:public Base {
};
template<Base& b>
void myfunction() {
//use b somehow
}
Derived myobject;
int main() {
myfunction<myobject>(); //this does not work
}
The code results in error message:
candidate template ignored: invalid explicitly-specified argument for template parameter 'b'
[live demo]
How do I pass a reference to a static instance of type Base, given the static Derived object myobject?
While it is fine to declare a template non-type parameter as a reference according to [temp.param]/4:
A non-type template-parameter shall have one of the following
(optionally cv-qualified) types:
...
lvalue reference to object or lvalue reference to function,
...
The argument must follow the restrictions in [temp.arg.nontype]/2:
A template-argument for a non-type template-parameter shall be a
converted constant expression of the type of the template-parameter.
For a non-type template-parameter of reference or pointer type, the
value of the constant expression shall not refer to (or for a pointer
type, shall not be the address of):
a subobject,
...
Which explicitly forbids what you are trying to do. Since b is going to end up referring to a sub-object.
The only solution which will make this compile, is adding another overload:
template<Derived & d>
void myfunction()
{
//use d somehow
}
So you'll need to extract the common code out somehow.
Or, if you have C++17 available:
template<auto& b, std::enable_if_t<
std::is_base_of_v<Base, std::decay_t<decltype(b)>>
, void*> = nullptr>
void myfunction()
{
//use b somehow
}
I suggest you re-think your general approach, however.

about std::result_of in c++11

As I know, a possible implementation of std::result_of is
template<class F, class... ArgTypes>
struct result_of<F(ArgTypes...)>
{
typedef decltype(
std::declval<F>()(std::declval<ArgTypes>()...)
) type;
};
But when I use std::result_of I have some trouble.
int f(int x)
{
return 0;
}
template<typename T>
void Test(const T& t)
{
decltype(std::declval<T>()(std::declval<int>())) i1 = 1; // ok
typename std::result_of<T(int)>::type i2 = 2; // compile error:
// function returning a function
// I think this means the same thing just as above, right?
}
int main()
{
Test(f);
return 0;
}
What are the differences between these two forms?
std::result_of is declared in C++11 [meta.trans.other] Table 57 as:
template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;
and it requires that:
Fn shall be a callable type (20.8.1), reference to function, or reference to callable type. The expression
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...))
shall be well formed.
callable type is defined in [func.def]/3:
A callable type is a function object type (20.8) or a pointer to member.
function object type is defined in [function.objects]/1:
A function object type is an object type (3.9) that can be the type of the postfix-expression in a function call (5.2.2, 13.3.1.1). ...
In your program, f is a reference to a function of type int(int), so T is deduced to the function type int(int). Note that a function type is not one of the valid options for the type Fn to be passed to std::result_type. A reference to function is an acceptable type, however: you should pass the full type of Tests parameter to result_of instead of only T (Demo at Coliru):
template<typename T>
void Test(const T&)
{
decltype(std::declval<T>()(std::declval<int>())) i1 = 1;
typename std::result_of<const T& (int)>::type i2 = 2;
}
Regarding the difference between the two forms, remember that std::declval always returns a reference type; specifically std::declval<T>() returns T&&. So
decltype(std::declval<T>()(std::declval<int>()))
is asking for the what type is returned when a T&& is invoked with an int&& argument.
[basic.compound] describes what function types look like in C++:
— functions, which have parameters of given types and return void or references or objects of a given type
Therefore, the return type part of a function type must not itself be a function type, and thus T(int) is not a valid type in the C++ type system when T = int(int).
Moreover, [dcl.fct]/8 clarifies:
Functions shall not have a return type of type array or function
Note also that the actual analogue of your i1 line is typename std::result_of<T>::type i2.

Strange use of void in c++ code

Here is some code from a presentation about async tasks in C++
template <class T> class Future<T>{
//something
void foo(std::function<void(T)> cb);
//something
};
What does void(T) mean?
What does void(T) mean?
That specifies a function type; specifically, a function taking a single parameter of type T, and returning nothing. In general, type specifiers for complex types look like their corresponding variable declarations, but without a variable name:
void f(T); // declare a function
void(T) // specifier for a function type
int a[42]; // declare an array
int[42] // specifier for an array type
In this case, the function type is used to specify the signature of the function-call operator of the std::function:
void operator()(T);
so that the function object can be used like a function with that type:
T some_t;
cb(some_t); // usable like a function taking `T`
cb is a std::function whose operator() takes a T and returns void.
void(T) is a function signature (type)
Note the signature is not a pointer to a function, not a pointer to a member function and no lambda:
#include<functional>
void a(int) {}
struct B {
void f(int) {}
};
int main() {
std::function<void(int)> fn;
fn = a;
B b;
fn = std::bind(&B::f, b, std::placeholders::_1);
auto lambda = [](int) {};
fn = lambda;
}
The explanation can be as follows based on the C++11 specifications : A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to an empty parameter list.
void is not a valid argument type for a function, however T in void(T) is a dependent type, it depends on a template parameter. That way you can have no arguments in the function based on the parameter of the template.