选择解析为相似签名的正确模板重载

问题描述 投票:0回答:0

TL;博士

有没有一种方法可以让我使用 SFINAE 选择函数模板的重载,该函数模板在解析时会产生类型参数的其他等效替换?


更新

根据要求,我制作了一个最小可复制示例(M.R.E.),可在此处完全获得:CPP.SH

但是,在这样做的过程中,我生成了一个示例,该示例并未反映我在实际代码中观察到的行为,因此我将尝试解释问题:

以下签名存在且相关:

#include <type_traits>
#include <iostream>

// Minimum reproducible example:

template <typename T>
class observable {
public:
    observable() = default;
    virtual ~observable() = default;

public:
    T read() const {
        std::cout << "Read " << m_value << ".\n";
        return m_value; 
    }
        
    void write(const T &value) { 
        std::cout << "Write " << value << ".\n";
        m_value = value; 
    }
    
private:
    T m_value;
};

template <typename T>
class iport : public virtual observable<T> {
public:
    iport() = default;
    virtual ~iport() = default;

// iport specific methods
};

template <typename T>
class oport : public virtual observable<T> {
public:
    oport() = default;
    virtual ~oport() = default;

// oport specific methods
};

template <typename T>
class ioport : public iport<T>, public oport<T> {
public:
    ioport() = default;
    virtual ~ioport() = default;
// ioport specific methods
};

// Overload 1
template<
    typename value_u, 
    typename value_v,
    typename = std::enable_if_t<
        std::is_convertible_v<value_u, value_v>>>
void operator<<(observable<value_u> &sink, const value_v& src) {
    // void observable<value_u>::write(const &value_u) is called
    // on src directly
    std::cout << "Using Overload 1\n";
    sink.write(src);
}

// Overload 2
template<
    typename value_u, 
    typename value_v,
    typename = std::enable_if_t<
        std::is_convertible_v<value_u, value_v>>>
void operator<<(observable<value_u> &sink, const observable<value_v>& src) {
    // void observable<value_u>::write(const &value_u) is called
    // after requesting a value via value_v observable<value_v>::read()
    std::cout << "Using Overload 2\n";
    sink.write(src.read());
}

int main(int, char*[]) {
    iport<int> src;
    ioport<int> mid;
    oport<int> sink;
    
    src << 2;
    mid << src;
    sink << mid;
}

在我的代码中,我正在调用(相当于)

sink << mid
,它无法编译并出现以下错误(解释):

[build] In instantiation of ‘void operator<<(observable<value_u>&, const value_v&) [with value_u = int; value_v = ioport<int>; <template-parameter-1-3> = void]’:
[build] error: cannot convert ‘const iport<int>’ to ‘const int&’
[build]    49 |   sink.write(src);
[build]       |   ~~~~~~~~~~^~~~~
[build] note:   initializing argument 1 of ‘void observable<T>::write(const T&) [with T = int]’
[build]    28 | void observable<T>::write(const T&value) {

在现实世界中,我的代码库试图将

ioport<int>
替换为
value_v
,这导致对
observable<int>::write(const int&)
的不兼容调用。

我不明白我给出的示例与链接中提供的 M.R.E 之间的差异。其他一切都是一样的,除了类型被重命名并且无关的实现被剥离。在重新考虑问题后,它的重点已经改变,尝试诊断错误替换发生的原因。

c++ c++11 sfinae
© www.soinside.com 2019 - 2024. All rights reserved.