Snippet:
#include <iostream>
template<typename T>
struct Printer{};
template<typename T>
void test(T&&)
{
std::cout << "Primary template called\n";
}
template<typename T>
void test(Printer<T>&&)
{
std::cout << "Specialized template called\n";
}
int main()
{
auto t = Printer<int>{};
test(0);
test(t);
}
Here is the demo
Why is two times Primary template called printed?
If one removes the forward reference from the second template, then the Printer overload is chosen.
Why is it not chosen with &&?
Forwarding reference only works for T&&, not C<T>&& nor const T&&.
test(0); // Call test(T&&) with T=int
test(t); // Call test(T&&) with T=Printer<int>&
Related
I'm implementing a templated boost static visitor that I want to use to return a type.
The visitor will be templated with one type and should throw an error for all other files.
#include <iostream>
#include <stdexcept>
#include <boost/variant/variant.hpp>
#include <boost/variant/static_visitor.hpp>
using namespace std;
template<class Type>
struct GetValue : public boost::static_visitor<Type>
{
template<class Other>
Type operator()(Other&& o) const
{
cout << "called some exception" << endl;
throw std::runtime_error("error");
}
Type operator()(Type& t) const
{
cout << "called correct" << endl;
return std::forward<Type>(t);
}
};
template<class S>
struct Foo : public boost::variant<S, std::string>
{
template <class Type>
Foo(Type&& t)
: boost::variant<Type, std::string>(std::forward<Type>(t))
{}
S operator*() const
{
return boost::apply_visitor(GetValue<S>{}, *this);
}
};
int main()
{
Foo<int> f = 5;
auto foo = *f;
return 0;
}
From what I understand, and sorry if I miss up the terminology, my first operator is a perfect forwarding reference and all calls will be sent there. However, my template type "Type" has already been expanded upon instantiation of the GetValue and thus no calls that I want match the second one that returns the type.
What definition do I need so that when I'm applying the visitor the function I want is called instead of everything referring to the default template.
This is the MVCE: https://godbolt.org/z/T5gd6m
I'm compiling in visual studio 2017 c++14.
Unless you want to provide an overload that can win overload resolution with a forwarding reference (that is, an lvalue reference Type& parameter), you can exclude the unwanted function from consideration through SFINAE:
#include <boost/type_traits.hpp>
template <class Type>
class GetValue : public boost::static_visitor<Type>
{
public:
template <class Other>
auto operator()(Other&& o) const
-> typename boost::disable_if<boost::is_same<typename boost::decay<Other>::type, Type>, Type>::type
{
throw error("Nope.");
}
Type operator()(const Type& t) const
{
return t;
}
};
DEMO
I have created a template function, defined below.
template<class T>
void func(T t){ /* do stuff */ }
I would like to overload this template in the event that T inherits from an abstract class I made.
class A {
public:
virtual void doStuff() = 0;
};
class B : public A {
virtual void doStuff(){ /* do stuff */ }
}
I have tried using template specialization (below), but it still was using the original definition.
template<>
void func(A& a){ /* do different stuff */ } // Not called by func(B())
I tried overloading it as well, and while this worked with ints, it isn't working with my base class.
func(int i){ /* do stuff with i */ } // Called by func(3)
func(A& a){ /* do different stuff */ } // Not called by func(B())
I'm guessing this has to do with C++ not wanting to implicitly cast my instance of B to an A and reference it, but I haven't been able to find anything explaining how I can fix this behavior. Since A has a pure virtual function, I can't just define func(A a). Any help would be appreciated.
Here is an example in which the behavior I'm experiencing can be reproduced.
#include <iostream>
template<class T>
void func(T t){
std::cout << "Template function called!" << std::endl;
}
class A {
public:
virtual void doStuff() = 0;
};
class B : public A{
public:
virtual void doStuff(){};
};
template<>
void func(const A& a){
std::cout << "Specialized template called!" << std::endl;
}
void func(const A& a){
std::cout << "Overload called!" << std::endl;
}
int main(){
B b{};
func(b);
return 0;
}
If you have access to C++17, then you can create a public overload that will forward to the correct function:
namespace detail {
template<class T>
void func(T t) {
std::cout << "Template function called!" << std::endl;
}
void func(A& a){
std::cout << "Overload called!" << std::endl;
}
}
template<class T>
void func(T& t) {
if constexpr (std::is_base_of_v<A, T>) {
detail::func(static_cast<A&>(t));
} else {
detail::func(t);
}
}
int main() {
B b;
func(b);
}
Otherwise you can use tag dispatching or SFINAE:
Tag dispatching:
template<class T>
void func(T t, std::false_type) {
std::cout << "Template function called!" << std::endl;
}
void func(A& a, std::true_type) {
std::cout << "Other function called!" << std::endl;
}
template<class T>
void func(T& t) {
func(t, std::is_base_of<A, T>{});
}
SFINAE:
template<class T,
std::enable_if_t<std::is_base_of_v<A, T>>* = nullptr>
void func(T t) {
std::cout << "Template function called!" << std::endl;
}
template<class T,
std::enable_if_t<!std::is_base_of_v<A, T>>* = nullptr>
void func(T& t) {
std::cout << "Other function called!" << std::endl;
}
C++20 solution
You can run the code here.
Using Concepts from C++20, we can write an inherits_from concept, and use that. Concepts allow us to constrain a template so that it only applies in situations where an expression is true.
The concept looks like this:
#include <type_traits>
template<class Derived, class Base>
concept derived_from = std::is_base_of_v<Base, Derived>;
Then, we can write the generic template and the constrained template:
struct MyBase{};
struct MyDerived : MyBase{};
// This is the generic template; using auto here is valid in C++20
void do_thing(auto const& thing) {
std::cout << "Doing thing on regular type\n";
}
//This is the template that acts on classes derived from MyBase
void do_thing(derived_from<MyBase> const& x) {
std::cout << "Doing thing on MyBase\n";
}
Because the second function declares T as following the concept inherits_from, it's more specialized, so for types that actually inherit from MyBase, it'll be selected over the generic template:
int main() {
do_thing(10); // Prints "Doing thing on regular type"
do_thing(MyBase()); // Prints "Doing thing on MyBase"
do_thing(MyDerived()); // Prints "Doing thing on MyBase"
}
C++17 solution
You can run the code here.
We can emulate the behavior of concepts using SFINAE, although this requires modifying the generic template so that it'll by ignored if T extends MyBase. The key to using SFINAE is to trigger a substitution failure if a condition is false, resulting in that overload being ignored.
In order to trigger a substitution failure, add a defaulted template argument at the end of the list of template parameters. In our case, it looks like this:
template<
class T,
// This defaulted argument triggers the substitution failure
class = std::enable_if_v</* condition */>>
In our code,
- The generic overload will be disabled if T extends MyBase
- The constrained overload will be disable if T doesn't extend MyBase
It looks like this:
struct MyBase {};
struct MyDerived : MyBase {};
template<
class T,
class = std::enable_if_t<!std::is_base_of_v<MyBase, T>>>
void do_thing(T const&) {
std::cout << "Doing thing on regular type\n";
}
// This overload is *disabled* if T doesn't inherit from MyBase
template<
class T,
// We have to have an additional defaulted template argument to distinguish between the overloads
class = void,
class = std::enable_if_t<std::is_base_of_v<MyBase, T>>>
void do_thing(T const& x) {
std::cout << "Doing thing on MyBase\n";
}
Despite the weird declaration, we can still use do_thing as though it were a regular function:
int main() {
do_thing(10);
do_thing(MyBase());
do_thing(MyDerived());
}
Backporting things to C++11
You can run the code here.
We only have to make a few minor changes to backport things to C++11. Basically,
is_base_of_v<MyBase, T> has to be replaced by is_base_of<MyBase, T>::value, and
enable_if_t</* condition */> has to be replaced by typename enable_if</* condition */>::type
B b;
func((A&)b);
I don't think you can pass a temporary here. You cannot cast B() to 'A&' because it is an rvalue and casting it to const A& makes the template version a better match.
Another options (on top of other answers) using c++11 - explicitly removing the template version if T is a subtype of B:
template<class T>
typename std::enable_if<!std::is_base_of<A, T>::value>::type
func(T& t){
std::cout << "Template function called!" << std::endl;
}
void func(const A& a){
std::cout << "Overload called!" << std::endl;
}
int main(){
func(B());
}
I try to use std::enable_if with an unused and unnamed type parameter, in order to not distort the return type. However, the following code does not compile.
#include <iostream>
template <typename T, typename = std::enable_if_t<!std::is_integral<T>::value>>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
The compiler says:
7:3: error: redefinition of 'template<class T, class> T foo()'
4:3: note: 'template<class T, class> T foo()' previously declared here
In function 'int main()':
11:12: error: no matching function for call to 'foo()'
11:12: note: candidate is:
4:3: note: template<class T, class> T foo()
4:3: note: template argument deduction/substitution failed:
What is the problem here? How do I have to change the code to get it compile? The text book "Discovering Modern C++" explicitly encourages the use of std::enable_if with anonymous type parameters.
EDIT: I know that it works if I put std::enable_if into the return type. However, my intention is to get some more details why it does not work if I use it with anonymous type parameters. As I said, my text book encourages the variant using anonymous type parameters, so I am wondering why my code does not compile.
However, my intention is to get some more details why it does not work if I use it with anonymous type parameters.
Default values do not participate in overload resolution, thus you are actually redefining the same function.
Let's simplify your example:
template<typename = int>
void f() {}
template<typename = void>
void f() {}
int main() {
f<>();
}
The code above does not compile, for it couldn't know what version of f you want to invoke.
In your case, if I invoke foo as foo<void, void>, I've almost the same problem.
The compiler cannot guess what's my intention and the fact that the second parameter has a default value doesn't mean that you can't pass in a different type.
Because of that, the code is ill-formed and the compiler correctly gives you an error.
As a side note, you can still have it working without using the std::enable_if_t in the return type.
As an example:
#include <type_traits>
#include <iostream>
template <typename T, std::enable_if_t<!std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
While I tried to figure out what was the (wrong) assumption of the OP and explain why it can be the case, #T.C. correctly pointed the attention out to the actual reason in the comments to this answer.
It's worth to quote his comment to add more details to the answer:
It's not overload resolution; it's declaration matching. There are no two overloads in the first place for any ambiguity to arise. It's two redefinition errors: the function template, and the default template argument.
You can put enable_if into the return type:
template <typename T>
std::enable_if_t<!std::is_integral<T>::value,T>
foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
std::enable_if_t<std::is_integral<T>::value, T>
foo() { std::cout << "integral" << std::endl; return T(); }
By the way, enable_if_t is available from C++14, so you might want to say typename std::enable_if<std::is_integral<T>::value, T>::type instead. Quite a mouthful.
But a little more idiomatic (and readable) would be to dispatch basing on the type:
template <typename T>
T foo_impl(std::false_type) { std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
T foo_impl(std::true_type) { std::cout << "integral" << std::endl; return T(); }
template <typename T>
T foo(){
return foo_impl<T>(typename std::is_integral<T>::type{});
}
There are a couple of ways you can SFINAE away functions. You should usually refrain from adding an extra function/template parameter and just mingle with the return type.
template <typename T>
auto foo() -> std::enable_if_t<!std::is_integral<T>::value, T>
{ std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
auto foo() -> std::enable_if_t<std::is_integral<T>::value, T>
{ std::cout << "integral" << std::endl; return T(); }
Your error is that you're using enable_if_t on the right of the equal sign.
You have to use it on the left
#include <iostream>
#include <type_traits>
template <typename T, std::enable_if_t<!std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
But this work with C++14.
In C++11 (your question is tagged C++11) you don't have enable_if_t.
The code become
#include <iostream>
#include <type_traits>
template <typename T,
typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
#include <iostream>
using namespace std;
template <typename T> class A{
public:
void test() { cout << "normal" << endl;}
};
//template <typename T> class A<T&>{
//public:
// void test() { cout << "&" << endl;}
//};
template <typename T> class A<T&&>{
public:
void test() { cout << "&&" << endl;}
};
int main(){
A<int&> a;
a.test();
}
The output is normal suggesting the ordinary (non-specialised) template was chosen. This may be seem obvious straight away since int & is supplied as a template argument and the only available specialisation is one that takes an rvalue reference. But why is it that the second template specialisation cannot be selected by choosing T = int& in which case reference collapsing causes T&& to become int&?
I want to overload two functions based on whether the argument is a temporary object, so I write code like this:
#include <iostream>
void f(int &&)
{
std::cout << "&&" << std::endl;
}
void f(const int&)
{
std::cout << "const &" << std::endl;
}
int main()
{
int i;
f(i);
f(i + 1);
}
And it corrently output:
const &
&&
However, when I change the code to use template like this:
#include <iostream>
template <typename T>
void f(T &&)
{
std::cout << "&&" << std::endl;
}
template <typename T>
void f(const T&)
{
std::cout << "const &" << std::endl;
}
int main()
{
int i;
f(i);
f(i + 1);
}
The output becomes:
&&
&&
What's the problem? How can I optimize for moveable temporary object when using template?
edit:
Actually, this is a test code when I read C++ Primer. It says:
template <typename T> void f(T&&); // binds to nonconst rvalues
template <typename T> void f(const T&); // lvalues and const rvalues
After my experiment, it seems the book makes a mistake here.
template <typename T>
void f(T &&)
{
std::cout << "&&" << std::endl;
}
Uses universal forwarding reference and allows any types with reference collapsing.
You have to use T with a no deducing context as wrapping your code into a struct:
template <typename T>
struct helper
{
void f(T &&)
{
std::cout << "&&" << std::endl;
}
void f(const T&)
{
std::cout << "const &" << std::endl;
}
};
template <typename T>
void f(T &&t)
{
helper<typename std::decay<T>::type>().f(std::forward<T>(t));
}
Live example