optimale typenübergabe



  • hallo

    da es bei kleinen eingebauten typen wie int usw schneller ist eine kopie zu erstellen anstatt eine kontante referenz zu nutzen, habe ich mir etwas kleines dazu zusammengebastelt:

    #include <limits>
    #include <iostream>
    #include <typeinfo>
    template<class T>
    class my_container
    {
    	template<class U, bool basic = std::numeric_limits<U>::is_specialized>
    	struct helper;
    public:
    	typedef typename helper<T>::type const_reference;
    };
    template<class T>
    template<class U> 
    struct my_container<T>::helper<U, true>
    {
    	typedef U type;
    };
    template<class T>
    template<class U> 
    struct my_container<T>::helper<U, false>
    {
    	typedef U const& type;
    };
    template<class T, class U>
    struct equal
    {
    	static const bool value = false;
    };
    template<class T>
    struct equal<T, T>
    {
    	static const bool value = true;
    };
    class special_type
    {
    	special_type(special_type const&);
    	special_type& operator= (special_type const&);
    public:
    	special_type()
    	{
    	}
    	~special_type()
    	{
    	}
    };
    int main()
    {
    	std::cout << std::boolalpha;
    	std::cout << "int as copy:\t\t" << equal<int, my_container<int>::const_reference>::value << '\n';
    	std::cout << "special_type as copy:\t" << equal<special_type, my_container<special_type>::const_reference>::value << '\n';
    	std::cout << "float as copy:\t\t" << equal<float, my_container<float>::const_reference>::value << '\n';
    }
    

    nun habe ich ein wenig angst da man std::numeric_limts spezialisieren kann und dann für die eigenen - unter umständen nicht kopierbaren typen - ein true bei is_specialized reintun kann. gibt es möglichkeiten dieses problem zu beheben? oder gibt es allgemein bessere methoden um zwischen (T) und (T const&) zu entscheiden bei konstanten referenzen?

    mfg





  • da es bei kleinen eingebauten typen wie int usw schneller ist eine kopie zu erstellen anstatt eine kontante referenz zu nutzen

    Es ist besser diese Ausgangsthese zu pruefen und die Groessenordnung von "schneller" bestimmen. Bei boost::call_traits habe ich keine Benchmarktabelle oder eingehende Analyse auf die Schnelle gefunden. Auch verstehe ich nicht, wo du jetzt in deinem Testfall tatsaechlich eine Kopie/Referenz erstellst.



  • danke krümelkacker, soetwas habe ich gesucht.

    @knivil, das habe ich mal vor einiger zeit so aufgeschnappt und seither ist es immer gang und gebe auch in anderen programmcodes, dass z.b. floats per call by copy (auch wenn sie in der funkion nicht geändert werden) übergeben werden und komplexere datenstrukutren mit mehreren membern per call by reference.



  • floats per call by copy (auch wenn sie in der funkion nicht geändert werden) übergeben werden und komplexere datenstrukutren mit mehreren membern per call by reference.

    Ja und?
    1.) Muss const-ref nicht langsamer sein bei primitiven Typen
    2.) Hat es vielleicht nix mit Performance zu tun sondern mit Schreibarbeit
    3.) Wie soll denn ein Funktionsaufruf nun konkret aussehen, d.h. wie soll ich dein Teil anwenden? Bis jetzt liefert es nur true/false als Template.
    4.) Der Compiler kann von selbst optimieren.



  • knivil schrieb:

    3.) Wie soll denn ein Funktionsaufruf nun konkret aussehen, d.h. wie soll ich dein Teil anwenden? Bis jetzt liefert es nur true/false als Template.

    ist doch offensichtlich, das true und false usw ist nur eine möglichkeit das darzustellen, der typ an und für sich ist das typedef const_reference in my_container<T>.

    anwenden würde man das dann so:

    template<class T>
    class my_container
    {
        template<class U, bool basic = std::numeric_limits<U>::is_specialized>
        struct helper;
    public:
        typedef typename helper<T>::type const_reference;
    
        const_reference get() const { /* ... */ }
        T& get() { /* ... */ }
    };
    


  • Weils so Spass macht:

    namespace detail {
      template <typename T>
      constexpr 
      typename std::conditional<true,bool,
                                decltype(T::advice_pass_by_value_tag)>::type
      is_tagged(int) { return T::advice_pass_by_value_tag; }
      template <typename T> constexpr bool is_tagged(...) { return false; }
    }
    template <typename T>
    struct advice_pass_by_value
      : std::integral_constant<bool,
                               (std::is_trivially_copyable<T>::value &&
                                sizeof(T) < 2*sizeof(T*))
        || detail::is_tagged<T>(0)> {};
    
    template <typename T>
    using copymized = typename std::conditional<advice_pass_by_value<T>::value, T, T const&>::type;
    
    /* Würde man so verwenden:
        copymized<T> get() const { ... }
        T& get() { ... }
    /*
    // ---
    
    class external_type { char blob[256]; };
    template <> struct advice_pass_by_value<external_type> : std::true_type {};
    
    struct integrated_type {
      static constexpr bool advice_pass_by_value_tag = true;
    };
    
    struct deepcopy_type {
      int data;
      deepcopy_type(deepcopy_type const&) {}
    };
    
    int main()
    {
    #define VERIFY(boolean, ...)                                                \
      static_assert(advice_pass_by_value<__VA_ARGS__>::value == boolean, #__VA_ARGS__)
    
      VERIFY(true, int);
      VERIFY(false, std::array<char,256>);
      VERIFY(true, external_type);
      VERIFY(true, integrated_type);
      VERIFY(false, deepcopy_type);
    }
    

    Aber ernsthaft: Jeder Compiler kann das heutzutage selber optimieren. In jedem Fall.

    Und get() muss aus Konsistenzgründen const& zurückggeben, damit du eine const-Referenz auf den originalen Wert bilden kannst.



  • ist doch offensichtlich, das true und false usw ist nur eine möglichkeit das darzustellen, der typ an und für sich ist das typedef const_reference in my_container<T>.

    Wenn es so offensichtlich ist, dann ist es fuer dich doch unproblematisch ein paar kleine Beispiele anzugeben. Ich bezweifle naemlich, das es im Vergleich ueberhaupt nutzbar ist.

    anwenden würde man das dann so

    Ich sehe immer noch keinen Funktionsaufruf. Nimm doch mal ein einfaches Beispiel mit double .

    double my_sqrt( .... x)
    {
        return std::sqrt(x ...);
    }
    

Anmelden zum Antworten