C++ 对不同概念的不同使用声明

C++ different using declarations for different concepts

比方说,我有我的 List<T> class。我有很多功能,我必须传递我的 T 类型的单个对象。例如

void add(const T& item)
{
    ...
}

如果 T 是某个 class 或一个结构,这是有道理的。但是,如果 T 是一个字节或整数,通过引用传递它是没有意义的,甚至是错误的,因为内存指针占用 8 个字节(32 位系统上为 4 个字节),即我通过 8 个字节传递 1 个字节大小的数据类型大小指针。

所以我决定使用 using 指令定义参数数据类型。种类:

using argType = const T&; requires sizeof(T) > 8
using argType = T; requires sizeof(T) <= 8

但是,显然,这段代码不起作用。您能否为此提出其他解决方案?

使用 c++20 概念,您可以向模板添加一些约束,

来自 en.cppreference.com/w/cpp/language/constraints

Class templates, function templates, and non-template functions (typically members of class templates) may be associated with a constraint, which specifies the requirements on template arguments, which can be used to select the most appropriate function overloads and template specializations. Named sets of such requirements are called concepts. Each concept is a predicate, evaluated at compile time, and becomes a part of the interface of a template where it is used as a constraint:

#include <string>
#include <cstddef>
#include <concepts>
 
// Declaration of the concept "Hashable", which is satisfied by any type 'T'
// such that for values 'a' of type 'T', the expression std::hash<T>{}(a)
// compiles and its result is convertible to std::size_t
template<typename T>
concept Hashable = requires(T a) {
    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
 
struct meow {};
 
// Constrained C++20 function template:
template<Hashable T>
void f(T) {}
//
// Alternative ways to apply the same constraint:
// template<typename T>
//    requires Hashable<T>
// void f(T) {}
//
// template<typename T>
// void f(T) requires Hashable<T> {}
 
int main() {
  using std::operator""s;
  f("abc"s); // OK, std::string satisfies Hashable
//f(meow{}); // Error: meow does not satisfy Hashable
}

在你的情况下,我认为你可以按如下方式限制你的模板:

template<typename T>
concept InfTo8Bytes = requires(T a) {
  sizeof (T) <= 8;
};

并按如下方式使用您的概念:

template<InfTo8Bytes T>
class Foo {

};

听起来你需要的是conditional_t:

#include <type_traits>

template<class T>
class List {
  using argType = std::conditional_t<(sizeof(T) > 8), const T&, T>;
  
  void add(argType item) { }
};

添加此处提供的答案: 其中建议 conditional_t

确保具有非平凡复制构造函数的项目通过引用传递很重要,因为值复制可能非常昂贵。因此,我会将按值传递限制为可简单复制的对象。

另外使用 sizeof(T*) 将比硬编码 8

更通用

注意args是颠倒的,测试是按值传递,sizeof == 8的对象如果需要仍然可以按引用传递。

using argType = std::conditional_t<
    (sizeof(T) <= sizeof(T *)) && std::is_trivially_copyable_v<T>,
    T, const T&>;

用法示例:

#include <type_traits>
#include <vector>
#include <iostream>

struct Test
{
    Test() : x{0}
    {
        std::cout << "Default construct" << std::endl;
    }
    
    Test(const Test& rhs) : x{rhs.x}
    {
        std::cout << "copy: " << x << std::endl;
    }
    int * x;
};

template<class T>
struct List {
  using argType = std::conditional_t<(sizeof(T) <= sizeof(T *)) && std::is_trivially_copyable_v<T>, T, const T&>;
  
  static argType copy(argType item)
    { 
        std::cout << "Running Copy" << std::endl;
        return item;
    }
};

auto foo(typename List<Test>::argType t)
{
    return List<Test>::copy(t);
}

auto goo(typename List<unsigned long long>::argType t)
{
    return List<unsigned long long>::copy(t);
}

int main()
{
    Test t;
    std::cout << "Calling Test Copy" << std::endl;
    foo(t);
    std::cout << "After Test Copy" << std::endl;

    unsigned long long u;
    std::cout << "Calling ULL Copy" << std::endl;
    goo(u);
    std::cout << "After ULL Copy" << std::endl;
}

在此处查看:https://godbolt.org/z/WTKT8G6rh 请注意,您可以在此处轻松查看 foo() 和 goo() 的参数

请注意 Test 如何按值传递并产生可能昂贵的副本,但检查昂贵的副本可防止这种情况发生