一个类型特征,标识哪个 class 提供重载决议选择的函数
A type trait identifying which class provides the function selected by overload resolution
考虑以下代码,其中仿函数 derived
继承自两个基 classes base1
和 base2
,每个都提供不同的重载:
// Preamble
#include <iostream>
#include <functional>
#include <type_traits>
// Base 1
struct base1 {
void operator()(int) const {
std::cout << "void base1::operator()(int) const\n";
}
void operator()(double) const {
std::cout << "void base1::operator()(double) const\n";
}
template <class Arg, class... Args>
void operator()(const Arg&, Args&&...) const {
std::cout << "void base1::operator()(const Arg&, Args&&...) const\n";
}
};
// Base 2
struct base2 {
void operator()(int) {
std::cout << "void base2::operator()(int)\n";
}
void operator()(double) {
std::cout << "void base2::operator()(double)\n";
}
};
// Derived
struct derived: base1, base2 {
using base1::operator();
using base2::operator();
void operator()(char) {
std::cout << "void derived::operator()(char)\n";
}
};
// Call
template <class F, class... Args>
void call(F&& f, Args&&... args) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
// Main
int main(int argc, char* argv[]) {
const derived d1;
derived d2;
derived d3;
call(d1, 1); // void base1::operator()(int) const
call(d2, 1); // void base2::operator()(int)
call(d1, 1, 2); // void base1::operator()(const Arg&, Args&&...) const
call(d2, 1, 2); // void base1::operator()(const Arg&, Args&&...) const
call(d3, 'a'); // void derived::operator()(char)
return 0;
}
结果输出为:
void base1::operator()(int) const
void base2::operator()(int)
void base1::operator()(const Arg&, Args&&...) const
void base1::operator()(const Arg&, Args&&...) const
void derived::operator()(char)
这说明根据参数,选定的重载可以来自 base1
、base2
或 derived
。
我的问题是:是否可以通过创建一个类型特征来在编译时做同样的事情,该特征将检测哪个 class 提供了选定的过载?
这将具有以下形式:
template <
class Base1, // Possibly cv-ref qualified base1
class Base2, // Possibly cv-ref qualified base2
class Derived, // Possibly cv-ref qualified derived
class... Args // Possibly cv-ref qualified args
>
struct overload_origin {
using base1 = std::decay_t<Base1>;
using base2 = std::decay_t<Base2>;
using derived = std::decay_t<Derived>;
using type = /* base1, base2, or derived * /
};
并且在前面示例代码中的 call
函数中使用时,overload_origin::type
会引用 base1
、base2
、base1
, base1
、derived
用于示例代码中说明的五个调用。
如何用模板元编程实现这样的事情?
您可以从 derived
和 base1
中导出 class。这样,来自 base1
的所有对 operator()
的调用都将是不明确的:
struct derived_check: base1, derived {
using base1::operator();
using base2::operator();
};
// Main
int main(int argc, char* argv[]) {
const derived_check d1;
derived_check d2;
derived_check d3;
call(d1, 1); // error:ambiguous
call(d2, 1); // OK
call(d1, 1, 2); // error:ambiguous
call(d2, 1, 2); // error:ambiguous
return 0;
}
然后您可以使用基本的检测技巧来创建您的检测类型特征。
考虑以下代码,其中仿函数 derived
继承自两个基 classes base1
和 base2
,每个都提供不同的重载:
// Preamble
#include <iostream>
#include <functional>
#include <type_traits>
// Base 1
struct base1 {
void operator()(int) const {
std::cout << "void base1::operator()(int) const\n";
}
void operator()(double) const {
std::cout << "void base1::operator()(double) const\n";
}
template <class Arg, class... Args>
void operator()(const Arg&, Args&&...) const {
std::cout << "void base1::operator()(const Arg&, Args&&...) const\n";
}
};
// Base 2
struct base2 {
void operator()(int) {
std::cout << "void base2::operator()(int)\n";
}
void operator()(double) {
std::cout << "void base2::operator()(double)\n";
}
};
// Derived
struct derived: base1, base2 {
using base1::operator();
using base2::operator();
void operator()(char) {
std::cout << "void derived::operator()(char)\n";
}
};
// Call
template <class F, class... Args>
void call(F&& f, Args&&... args) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
// Main
int main(int argc, char* argv[]) {
const derived d1;
derived d2;
derived d3;
call(d1, 1); // void base1::operator()(int) const
call(d2, 1); // void base2::operator()(int)
call(d1, 1, 2); // void base1::operator()(const Arg&, Args&&...) const
call(d2, 1, 2); // void base1::operator()(const Arg&, Args&&...) const
call(d3, 'a'); // void derived::operator()(char)
return 0;
}
结果输出为:
void base1::operator()(int) const
void base2::operator()(int)
void base1::operator()(const Arg&, Args&&...) const
void base1::operator()(const Arg&, Args&&...) const
void derived::operator()(char)
这说明根据参数,选定的重载可以来自 base1
、base2
或 derived
。
我的问题是:是否可以通过创建一个类型特征来在编译时做同样的事情,该特征将检测哪个 class 提供了选定的过载?
这将具有以下形式:
template <
class Base1, // Possibly cv-ref qualified base1
class Base2, // Possibly cv-ref qualified base2
class Derived, // Possibly cv-ref qualified derived
class... Args // Possibly cv-ref qualified args
>
struct overload_origin {
using base1 = std::decay_t<Base1>;
using base2 = std::decay_t<Base2>;
using derived = std::decay_t<Derived>;
using type = /* base1, base2, or derived * /
};
并且在前面示例代码中的 call
函数中使用时,overload_origin::type
会引用 base1
、base2
、base1
, base1
、derived
用于示例代码中说明的五个调用。
如何用模板元编程实现这样的事情?
您可以从 derived
和 base1
中导出 class。这样,来自 base1
的所有对 operator()
的调用都将是不明确的:
struct derived_check: base1, derived {
using base1::operator();
using base2::operator();
};
// Main
int main(int argc, char* argv[]) {
const derived_check d1;
derived_check d2;
derived_check d3;
call(d1, 1); // error:ambiguous
call(d2, 1); // OK
call(d1, 1, 2); // error:ambiguous
call(d2, 1, 2); // error:ambiguous
return 0;
}
然后您可以使用基本的检测技巧来创建您的检测类型特征。