Một cách thời gian biên dịch để xác định loại đối số ít tốn kém nhất


15

Tôi có một mẫu trông như thế này

template <typename T> class Foo
{
public:
    Foo(const T& t) : _t(t) {}
private:
    const T _t;
};

Có một cách hiểu siêu lập trình mẫu để tránh sử dụng tham chiếu const trong trường hợp kiểu đối số không quan trọng như bool hoặc char? giống:

Foo(stl::smarter_argument<T>::type t) : _t(t) {}

1
Tôi sẽ không lo lắng về nó, nếu hàm nhỏ, trình biên dịch sẽ nội tuyến và tham chiếu thậm chí sẽ không tồn tại. Nếu hàm lớn, chi phí nhỏ để bọc một số nguyên vào tham chiếu sẽ không đáng kể
Alan Birtles

1
Tôi sẽ lo lắng nhiều hơn về chuyển tiếp hoàn hảo sau đó tránh các tài liệu tham khảo về các loại dữ liệu nhỏ. Tôi đoán rằng việc chuyển qua tham chiếu giá trị r có thể được tối ưu hóa thành từng giá trị trong hầu hết các trường hợp.
siêu

Một điều cần ghi nhớ, không được chỉ ra trong các câu trả lời: những gì bạn đang làm sẽ đánh bại các hướng dẫn khấu trừ ngầm. Bạn nên nhớ viết một hướng dẫn khấu trừ rõ ràng nếu bạn quan tâm đến việc khấu trừ đối số mẫu lớp làm việc cho Foo.
Brian

Câu trả lời:


13

Tôi nghĩ rằng đặc điểm loại đúng là is_scalar. Điều này sẽ làm việc như sau:

template<class T, class = void>
struct smarter_argument{
    using type = const T&;
};

template<class T>
struct smarter_argument<T, std::enable_if_t<std::is_scalar_v<T>>> {
    using type = T;
};

Biên tập:

Ở trên vẫn còn một chút trường học cũ, cảm ơn @HolyBlackCat đã nhắc nhở tôi về phiên bản ngắn gọn hơn này:

template<class T>
using smarter_argument_t = std::conditional_t<std::is_scalar_v<T>, T, const T&>;

cũng sẽ không is_fundamentallàm việc?
Tarek Dakhran

2
Vô hướng @TarekDakhran bao gồm các con trỏ và enum không phải là cơ bản, nên được truyền bằng giá trị IMO.
LF

Tôi không quen với cú pháp class = void. Điều đó có nghĩa là nó có thể là bất cứ điều gì vì nó bị bỏ qua?
cppguy

1
= voidcó nghĩa là nó có một loại mặc định là void, vì vậy việc sử dụng smarter_argument<T>là thực sự smarter_argument<T, void>. Tôi đã để lại một tên cho đối số này vì chúng tôi không cần nó, do đó class = voidkhông có tên. Điều quan trọng là std::enable_if_ttrong trường hợp nó được kích hoạt cũng phải có hiệu lực để phù hợp với loại mặc định.
n314159

2
Có thể được đơn giản hóa để template <typename T> using smarter_argument = std::conditional_t<std::is_scalar_v<T>, T, const T &>;.
HolyBlackCat

3

Tôi sẽ đề nghị sử dụng sizeof(size_t)(hoặc sizeof(ptrdiff_t)) trả về kích thước "điển hình" liên quan đến máy của bạn với hy vọng rằng bất kỳ biến nào có kích thước này phù hợp với một thanh ghi. Trong trường hợp đó bạn có thể vượt qua nó một cách an toàn. Hơn nữa, như được đề xuất bởi @ n314159 (xem bình luận ở cuối bài này), rất hữu ích để đảm bảo rằng biến cũng là trivialy_copyable.

Đây là bản demo C ++ 17:

#include <array>
#include <ccomplex>
#include <iostream>
#include <type_traits>

template <typename T>
struct maybe_ref
{
  using type = std::conditional_t<sizeof(T) <= sizeof(size_t) and
                                  std::is_trivially_copyable_v<T>, T, const T&>;
};

template <typename T>
using maybe_ref_t = typename maybe_ref<T>::type;

template <typename T>
class Foo
{
 public:
  Foo(maybe_ref_t<T> t) : _t(t)
  {
    std::cout << "is reference ? " << std::boolalpha 
              << std::is_reference_v<decltype(t)> << std::endl;
  }

private:
  const T _t;
};

int main()
{
                                                          // with my machine
  Foo<std::array<double, 1>> a{std::array<double, 1>{}};  // <- by value
  Foo<std::array<double, 2>> b{std::array<double, 2>{}};  // <- by ref

  Foo<double>               c{double{}};                // <- by value
  Foo<std::complex<double>> d{std::complex<double>{}};  // <- by ref
}

Lưu ý rằng không có thứ gọi là "kích thước con trỏ của máy bạn". Chạy ví dụ này : struct Foo { void bar(){ }; int i; }; std::cout << sizeof(&Foo::i) << std::endl; //prints 8 std::cout << sizeof(&Foo::bar) << std::endl; //prints 16
BlueTune

@BlueTune Thú vị, cảm ơn vì nhận xét. Đồng thời xem stackoverflow.com/a/6751914/2001017 như ví dụ của bạn cho thấy: con trỏ và con trỏ hàm có thể có kích thước khác nhau. Ngay cả con trỏ khác nhau có thể có kích thước khác nhau. Ý tưởng là để có được kích thước "điển hình" của máy. Tôi đã thay thế sizeof mơ hồ (void *) bằng sizeof (size_t)
Picaud Vincent

1
@Picaud có thể bạn muốn sử dụng <=thay vì ==, trên hầu hết các máy, mã hiện tại của bạn lấy charví dụ bằng cách tham chiếu nếu tôi thấy đúng.
n314159

2
Bạn cũng có thể muốn kiểm tra xem có Tthể sao chép một cách tầm thường. Ví dụ, một con trỏ được chia sẻ chỉ có kích thước gấp đôi size_ttrên biểu đồ của tôi và nó có thể được thực hiện chỉ bằng một con trỏ, đưa nó xuống cùng kích thước. Nhưng bạn chắc chắn muốn dùng shared_ptr bằng const ref chứ không phải theo giá trị.
n314159

@ n314159 có đó sẽ là một cải tiến. Bạn có ổn không nếu bao gồm ý tưởng của bạn trong câu trả lời của tôi?
Picaud Vincent

2

Tôi sẽ sử dụng từ khóa C ++ 20 requires. Chỉ như thế:

#include <iostream>

template<typename T>
class Foo
{
public:
    Foo(T t) requires std::is_scalar_v<T>: _t{t} { std::cout << "is scalar" <<std::endl; }
    Foo(const T& t) requires (not std::is_scalar_v<T>): _t{t} { std::cout << "is not scalar" <<std::endl;}
private:
    const T _t;
};

class cls {};

int main() 
{
    Foo{true};
    Foo{'d'};
    Foo{3.14159};
    cls c;
    Foo{c};

    return 0;
}

Bạn có thể chạy mã trực tuyến để xem đầu ra sau:

is scalar
is scalar
is scalar
is not scalar

Hấp dẫn. Có lợi ích gì khi sử dụng const auto & cho đối số constructor không?
cppguy

@cppguy: Tôi rất vui khi bạn hỏi câu hỏi đó. Nếu tôi thay thế đối số "const auto & t" bằng "const T & t" thì mã sẽ không biên dịch. Lỗi này ghi "... suy luận mơ hồ cho các đối số mẫu của 'Foo' ...". Có lẽ bạn có thể tìm hiểu tại sao?
BlueTune

1
@cppguy: Cuộc thảo luận của chúng tôi dẫn đến một câu hỏi tôi đặt ra. Bạn có thể tìm thấy nó ở đây .
BlueTune

1
Các khái niệm là quá mức cần thiết ở đây và thực sự khó đọc hơn so với thay thế.
SS Anne

1
@SS Anne: IMHO sử dụng các khái niệm C ++ 20 không bao giờ là quá mức cần thiết. Nó chỉ là thanh lịch. IMHO các lựa chọn thay thế mà tôi đã thấy cho đến nay khó đọc hơn, bởi vì việc sử dụng các mẫu lồng nhau.
BlueTune
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.