Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change template instantiate order?

I want to change the instantiate order of the overload templates. My code are as follow:

#include <iostream>
#include <vector>

using namespace std;
template<typename T>
struct Base
{

};
template<typename T>
struct Derived:Base<T>
{

};

//template 1
template<typename T1>
void f(Base<T1>& a){
    cout<<"in 1\n";
}

//template 2
template<typename T2>
void f(T2 b){
    cout<<"in 2\n";
}

int main(){
    Derived<int> v1;
    f(v1);
}

The compiler will chose template 2 as default, but I want it to chose template 1.

like image 554
maple Avatar asked Jan 20 '26 12:01

maple


2 Answers

With custom traits

template <typename T> std::true_type derive_from_base_impl(const Base<T>*);
std::false_type derive_from_base_impl(...);

template <typename T>
using derive_from_base = decltype(derive_from_base_impl(std::declval<T*>()));

static_assert(!derive_from_base<int>::value, "");
static_assert(derive_from_base<Derived<int>>::value, "");

And SFINAE, you may do

template<typename T>
void f(Base<T>& a) { std::cout << "in 1\n"; }

template<typename T>
std::enable_if_t<!derive_from_base<T>::value>
f(T b) { std::cout << "in 2\n"; }

Demo

like image 140
Jarod42 Avatar answered Jan 23 '26 04:01

Jarod42


The right term for this is overload resolution preferences, and second overload is chosen because it is a better match. It appeared to be more challenging than I expected to put together a code which would make sure proper overload was selected. Here it is:

#include <type_traits>
#include <iostream>

template<typename T>
struct Base { };
template<typename T>
struct Derived:Base<T> { };

template <class T>
struct template_argument { using type = void*; };

template <template <class > class T, class ARG>
struct template_argument<T<ARG> > { 
    using type = ARG;
};

template <class T>
using template_argument_t = typename template_argument<T>::type;

template<typename T1>
void f(Base<T1>& ){
    std::cout << "Base-expecting f called\n";
}

template<class T>
void f(T, std::enable_if_t<!std::is_base_of<Base<template_argument_t<T> >, T>::value>* = nullptr ) {
    std::cout << "Generic f was called.\n";
}

template <class T>
struct Z { };

int main(){
    Derived<int> v1;

    f(v1);
    f(int() );
    f(Z<int>() );
}

Output:

Base-expecting f called

Generic f was called.

Generic f was called.

like image 35
SergeyA Avatar answered Jan 23 '26 03:01

SergeyA



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!