En ucuz argüman türünü belirlemenin derleme zamanı yolu


15

Şuna benzeyen bir şablonum var

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

Argüman türünün bool veya char gibi önemsiz olduğu durumlarda const referansı kullanmaktan kaçınmak için anlayışlı bir şablon metaprogramlama yolu var mı? sevmek:

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

1
Bu konuda endişe etmem, eğer fonksiyon küçükse derleyici satır içi olacak ve referans bile olmayacak. İşlev büyükse, bir tamsayıyı referansa
sarmanın

1
Mükemmel veri iletme konusunda endişelenirim ve daha sonra küçük veri türlerine ilişkin referanslardan kaçınırım. R-değeri referansı ile geçmenin çoğu durumda by-pass değerine göre optimize edilebileceğini tahmin ediyorum.
Süper

Cevaplarda belirtilmeyen, akılda tutulması gereken bir şey: yaptığınız şey örtülü kesinti kılavuzlarını yenecektir. Eğer sınıf şablonu argüman kesinti için çalışıyorsanız, açık bir kesinti kılavuzu yazmayı unutmayın Foo.
Brian

Yanıtlar:


13

Bence doğru tip özellik is_scalar. Bu aşağıdaki gibi çalışır:

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

Düzenle:

Yukarıdaki hala biraz eski okul, bana bu daha kısa versiyonu hatırlattığınız için @HolyBlackCat teşekkürler:

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

da is_fundamentalişe yaramaz mı?
Tarek Dakhran

2
@TekekDakhran skaler, IMO değerinden geçirilmesi gereken temel olmayan işaretçiler ve numaralandırmalar içerir.
LF

Class = void sözdizimine aşina değilim. Bu, göz ardı edildiği için herhangi bir şey olabileceği anlamına mı geliyor?
cppguy

1
= voidvasıtaları o kadar kullanarak, geçersizdir varsayılan bir türüne sahip smarter_argument<T>aslında smarter_argument<T, void>. Bu argüman için bir isim bıraktım çünkü ona ihtiyacımız yok, dolayısıyla isimsiz class = void. Önemlidir std::enable_if_tdurumda o da boş olmalıdır etkin olduğunu varsayılan türüyle eşleşmesi için.
n314159

2
Basitleştirilebilir template <typename T> using smarter_argument = std::conditional_t<std::is_scalar_v<T>, T, const T &>;.
HolyBlackCat

3

Bu boyuttaki herhangi bir değişkenin bir kayıt defterine sığması ümidiyle makinenizle ilgili "tipik" bir boyut döndüren sizeof(size_t)(veya sizeof(ptrdiff_t)) kullanmanızı öneririm . Bu durumda, değere göre güvenle geçirebilirsiniz. Ayrıca, @ n314159 tarafından önerildiği gibi (bu yazının sonundaki yorumlara bakınız), değişkenin de olduğundan emin olmak yararlıdır trivialy_copyable.

İşte bir C ++ 17 demosu:

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

"Makinenizin işaretçi boyutu" diye bir şey olmadığını unutmayın. Örneğin şunu çalıştırın : 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 İlginç, yorum için teşekkürler. Ayrıca örnekte gösterildiği gibi stackoverflow.com/a/6751914/2001017 adresine bakın : işaretçiler ve işlev işaretçileri farklı boyutlarda olabilir. Farklı işaretçiler bile farklı boyutlara sahip olabilir. Fikir, makinenin "tipik" boyutunu elde etmekti. Belirsiz sizeof (void *) sizeof (size_t) ile değiştirildi
Picaud Vincent

1
@Picaud belki kullanmak istediğiniz <=yerine ==geçerli kodu alır çoğu makinelerde, charbunun hakkını görürsem referans olarak örneğin.
n314159

2
Ayrıca Tönemsiz bir şekilde kopyalanabilir olup olmadığını kontrol etmek isteyebilirsiniz . Örneğin, paylaşılan bir işaretçi size_tplatformumdaki boyutun yalnızca iki katıdır ve yalnızca bir işaretçiyle aynı boyuta getirilerek uygulanabilir. Ama kesinlikle share_ptr değerini const ref ile değil, değerle almak istersiniz.
n314159

@ n314159 evet bu bir gelişme olurdu. Fikrimi yanıtıma eklerseniz iyi misiniz?
Picaud Vincent

2

C ++ 20 anahtar kelimesini kullanırdım requires. Aynen böyle:

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

Aşağıdaki çıktıyı görmek için kodu çevrimiçi çalıştırabilirsiniz :

is scalar
is scalar
is scalar
is not scalar

İlginç. Const auto & yapıcı argümanı için kullanmanın bir yararı var mı?
cppguy

@cppguy: Bu soruyu sormana sevindim. "Const auto & t" argümanını "const T & t" ile değiştirirsem kod derlenmez. Hata "... Foo" şablon argümanları için belirsiz bir kesinti. Belki nedenini bulabilirsin?
BlueTune

1
@cppguy: Tartışmamız, sorduğum bir soru ile sonuçlandı. Burada bulabilirsiniz .
BlueTune

1
Kavramlar burada aşırıya kaçıyor ve alternatiften çok daha zor okunuyor.
SS Anne

1
@SS Anne: C ++ 20 kavramlarını kullanan IMHO asla aşırıya kaçmaz. Sadece zarif. IMHO şimdiye kadar gördüğüm alternatifleri okumak daha zordur, çünkü iç içe şablonların kullanımı.
BlueTune
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.