가장 저렴한 인수 유형을 결정하는 컴파일 시간 방법


15

다음과 같은 템플릿이 있습니다

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

인수 유형이 부울 또는 문자와 같이 사소한 경우 const 참조를 사용하지 않는 정통한 템플릿 메타 프로그래밍 방법이 있습니까? 처럼:

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

1
함수가 작 으면 컴파일러가 인라인하고 참조가 존재하지 않을 것이므로 걱정하지 않아도됩니다. 함수가 크면 정수를 참조로 래핑하는 데 드는 작은 비용은 중요하지 않습니다.
Alan Birtles

1
작은 데이터 형식에 대한 참조를 피하고 완벽한 전달에 대해 더 걱정할 것입니다. r 값 참조로 전달하는 것이 대부분의 경우 값으로 전달되도록 최적화 될 수 있다고 생각합니다.
슈퍼

답변에서 지적하지 않은 점을 명심하십시오. 당신이하는 일이 암묵적 추론 가이드를 물리 칠 것입니다. 클래스 템플릿 인수 공제 작업에 관심이있는 경우 명시 적 공제 안내서를 작성해야합니다 Foo.
브라이언

답변:


13

나는 올바른 유형의 특성이 있다고 생각합니다 is_scalar. 이것은 다음과 같이 작동합니다.

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;
};

편집하다:

위의 내용은 여전히 ​​약간 오래된 학교입니다. @ HolyBlackCat이 더 간결한 버전을 상기시켜 주셔서 감사합니다.

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

is_fundamental작동 하지 않습니까?
Tarek Dakhran

2
@TarekDakhran 스칼라에는 기본적이지 않은 포인터와 열거 형이 포함되어 있으며 값 IMO에 의해 전달되어야합니다.
LF

class = void 구문에 익숙하지 않습니다. 그것은 무시되기 때문에 무엇이든 될 수 있다는 것을 의미합니까?
cppguy

1
= void는 기본 유형이 void임을 의미하므로 smarter_argument<T>실제로는 smarter_argument<T, void>입니다. 나는 우리가 그것을 필요로하지 않기 때문에이 주장에 대한 이름을 생략했다 class = void. 그러므로 이름이 없다. 것이 중요 std::enable_if_t경우에도 무효가 있어야 사용할 수 있는지가 기본 유형과 일치하는.
17:06의

2
로 단순화 할 수 있습니다 template <typename T> using smarter_argument = std::conditional_t<std::is_scalar_v<T>, T, const T &>;.
HolyBlackCat

3

이 크기의 모든 변수가 레지스터에 적합하기를 희망하면서 컴퓨터와 관련된 "일반적인"크기를 반환하는 sizeof(size_t)(또는 sizeof(ptrdiff_t)) 를 사용하는 것이 좋습니다 . 이 경우 값으로 안전하게 전달할 수 있습니다. 또한 @ n314159 (이 게시물의 끝 부분에있는 주석 참조)에서 제안한 것처럼 변수도 역시 확인하는 것이 유용합니다 trivialy_copyable.

다음은 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
}

"컴퓨터의 포인터 크기"와 같은 것은 없습니다. 예를 들어 다음과 같이 실행하십시오 . 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 흥미로운 의견에 감사드립니다. 예제와 같이 stackoverflow.com/a/6751914/2001017 도 참조하십시오 . 포인터와 함수 포인터의 크기는 다를 수 있습니다. 다른 포인터조차도 크기가 다를 수 있습니다. 아이디어는 기계의 "일반적인"크기를 얻는 것이 었습니다. 모호한 sizeof (void *)를 sizeof (size_t)로 대체했습니다.
Picaud Vincent

1
@Picaud <=대신에 대신 사용하고 싶을 수도 있습니다 ==. 대부분의 컴퓨터에서 현재 코드는 char예를 들어 참조로 사용합니다.
n314159

2
또한 T사소한 복사가 가능한지 확인하고 싶을 수도 있습니다 . 예를 들어 공유 포인터는 size_t내 플랫폼 의 크기의 두 배에 불과하며 하나의 포인터로 구현하여 동일한 크기로 가져올 수 있습니다. 그러나 값이 아닌 const ref로 shared_ptr을 사용하려고합니다.
n314159

@ n314159 예, 개선 될 것입니다. 내 대답에 아이디어를 포함 시켜도 괜찮습니까?
Picaud Vincent

2

C ++ 20 키워드를 사용 requires합니다. 그냥 이렇게 :

#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;
}

온라인 으로 코드를 실행 하여 다음 출력을 볼 수 있습니다 .

is scalar
is scalar
is scalar
is not scalar

흥미 롭군 생성자 인수에 const auto &를 사용하면 이점이 있습니까?
cppguy

@ cppguy : 그 질문에 기뻐요. "const auto & t"인수를 "const T & t"로 바꾸면 코드가 컴파일되지 않습니다. 오류는 "... 템플릿 인수 'Foo'에 대한 모호한 추론 ..."을 읽습니다. 어쩌면 당신은 이유를 찾을 수 있습니까?
BlueTune

1
@ cppguy : 우리의 토론은 내가 제기 한 질문으로 이어졌습니다. 여기에서 찾을 수 있습니다 .
BlueTune

1
여기서 개념은 과잉이며 대안보다 읽기 어렵습니다.
SS Anne

1
@SS Anne : C ++ 20 개념을 사용한 IMHO는 결코 지나치지 않습니다. 그냥 우아합니다. IMHO 지금까지 본 대안은 중첩 템플릿을 사용하기 때문에 읽기가 어렵습니다.
BlueTune
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.