长话短说
非模板化类中的几个模板化和重载的非模板化成员函数最终应该通过相同的成员函数路由来执行实际工作。所有重载和模板化都是为了将“数据缓冲区”转换为 gsl::span<std::byte>
。类型(本质上是来自 Guidelines Support Library 的 std::array<std::byte, N>
的近亲)
代码墙
#include <array>
#include <cstdlib>
#include <iostream>
#pragma warning(push)
#pragma warning(disable: 4996)
#include <gsl.h>
#pragma warning(pop)
// convert PoD into "memory buffer" for physical I/O
// ignore endianness and padding/alignments for this example
template<class T> gsl::span<std::byte> byte_span(T& _x) {
return { reinterpret_cast<std::byte*>(&_x), sizeof(T) };
}
// implementation of physical I/O (not a functor, but tempting)
struct A {
enum class E1 : uint8_t { v1 = 10, v2, v3, v4 };
bool f(uint8_t _i1, gsl::span<std::byte> _buf = {}); // a - "in the end" they all call here
bool f(E1 _i1, gsl::span<std::byte> _buf = {}); // b
template<typename T, typename = std::enable_if_t< std::is_integral<T>::value > >
bool f(uint8_t _i1, T& _val); // c
template<typename T, typename = std::enable_if_t< std::is_integral<T>::value > >
bool f(E1 _i1, T& _val); // d
};
bool A::f(uint8_t _i1, gsl::span<std::byte> _buf)
{
std::cout << "f() uint8_t{" << (int)_i1 << "} with " << _buf.size() << " elements\n";
return true;
}
bool A::f(E1 _i1, gsl::span<std::byte> _buf)
{
std::cout << "f() E1{" << (int)_i1 << "} with " << _buf.size() << " elements\n\t";
return f((uint8_t)_i1, _buf);
}
template<class T, typename>
bool A::f(uint8_t _i1, T& _val)
{
std::cout << "template uint8_t\n\t";
return f(_i1, byte_span(_val));
}
template<class T, typename>
bool A::f(E1 _i1, T& _val)
{
std::cout << "template E1\n\t";
return f(_i1, byte_span(_val));
}
int main(){
A a = {};
std::array<std::byte, 1> buf;
long i = 2;
// regular function overloads
a.f(1, buf); // should call (a)
a.f(A::E1::v1, buf); // should call (b)
// template overloads
a.f(2, i); // should call (c)
a.f(A::E1::v2, i); // should call (d)
struct S { short i; };
// issue
//S s;
//a.f(3, s); // should call (c)
//a.f(A::E1::v3, s); // should call (d)
//// bonus - should use non-template overrides
//S sv[2] = {};
//a.f(5, sv); // should call (a)
//a.f(A::E1::v1, sv); // should call (b)
}
详情
struct S
是一个 PoD,很容易更改模板的 enable_if
使用std::is_trivial
或 std::is_standard_layout
.不幸的是,这两种解决方案都“抢得太多”并最终匹配 std::array
(即使他们确实修复了 //issue
block 的编译错误)。
我现在的解决方案看起来像死胡同,因为我的直觉是开始添加更多的模板参数,而且它似乎很快就会变得非常毛茸茸:(
问题
我的目标是实现以下目标:使用 class A
的 bool f()
任何 PoD 没有太多语法开销的成员函数(可能包括 C 数组 - 请参阅代码中的“奖励”),如 main()
的正文所示对于可自动转换为 gsl::span
的类型,没有运行时函数调用开销(如 std::array
和 std::vector
)。
理想情况下...
我希望每个第一个参数(E1
或 uint8_t
)有一个模板化函数,并在类主体之外列出多个特化,以进一步减少类声明中可感知的代码困惑,但我不能想办法正确地做到这一点。类似于以下内容(下面是非法 C++ 代码!):
struct A {
// ...
template<typename T> bool f(uint8_t _i1, T& _val);
template<typename T> bool f(E1 _i1, T& _val);
};
template<> bool f<is_PoD<T> && not_like_gsl_span<T>>(uint8_t /*...*/}
template<> bool f<is_PoD<T> && not_like_gsl_span<T>>(E1 /*...*/}
template<> bool f<is_like_gsl_span<T>>(uint8_t /*...*/}
template<> bool f<is_like_gsl_span<T>>(E1 /*...*/}
如果这无法实现,我想知道原因。
我正在使用启用了 C++17 的 MSVC 2017。
最佳答案
第一个回答有点错误,不知道为什么,我完全被gsl搞糊涂了。我认为这是非常具体的事情。我没有使用 Guideline Support Library,尽管我已经看到它并且看起来不错。
修复了代码以正确处理 gsl::span
类型。
struct A {
enum class E1 : uint8_t { v1 = 10, v2, v3, v4 };
private:
template <typename T>
static auto make_span(T& _x) ->
typename std::enable_if<std::is_convertible<T&, gsl::span<std::byte>>::value,
gsl::span<std::byte>>::type {
std::cout << "conversion" << std::endl;
return _x;
}
template <typename T>
static auto make_span(T& _x) ->
typename std::enable_if<!std::is_convertible<T&, gsl::span<std::byte>>::value,
gsl::span<std::byte>>::type {
std::cout << "cast" << std::endl;
return {reinterpret_cast<std::byte*>(&_x), sizeof(T)};
}
public:
template <typename T, typename U>
bool f(T _i, U& _buf) {
static_assert(
std::is_convertible<U&, gsl::span<std::byte>>::value || std::is_trivial<U>::value,
"The object must be either convertible to gsl::span<std::byte> or be of a trivial type");
const auto i = static_cast<uint8_t>(_i);
const auto span = make_span(_buf);
std::cout << "f() uint8_t{" << (int)i << "} with " << span.size() << " elements\n";
return true;
}
};
关于c++ - 使用 SFINAE 的非模板类中的模板函数重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51126360/