How can a variadic template be used to generate a left-associative expression (aka left fold) in c++11?












11















I would like to use a c++ template to aggregate (fold) multiple arguments using a binary operation.



Such a template could be used as follows:



fold<add>(100,10,5) expands to add(add(100, 10), 5)



The particular expansion shown above is the "left fold". The expansion add(100, add(10, 5)) is the "right fold". Assuming that the add function performs simple integer addition, both right and left folds produce the same result, 115.



But consider a function div that performs integer division (div(a,b)=a/b). In this case, associativity matters and the left and right folds produce different results:



fold_left<div>(100,10,5)  --> div(div(100, 10), 5) --> div(10, 5) -->  2
fold_right<div>(100,10,5) --> div(100, div(10, 5)) --> div(100, 2) --> 50


It is straightforward to use a variadic template to produce the right-associative version (fold_right), but I have not been able to figure out how to produce the left-associative version (fold_left). The attempted implementation of fold_left below results in a compiler error:



#include <iostream>

template <typename T> using binary_op = T(*)(const T& a, const T& b);

// The terminal (single-argument) cases of the variadic functions defined later.
template<typename T, binary_op<T> Operation> inline T fold_right(const T& t) { return t; }
template<typename T, binary_op<T> Operation> inline T fold_left(const T& t) { return t; }

// Combines arguments using right-associative operation
// i.e. fold_right<T,op>(A,B,C) --> op(A, op(B,C))
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_right(const T& t, Rest... rest) {
return Operation(t, fold_right<T, Operation>(rest...));
}

// Combines arguments using left-associative operation
// i.e. fold_left<T,op>(A,B,C) --> op(op(A,B), C)
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(Rest... rest, const T& t) {
return Operation(fold_left<T, Operation>(rest...), t);
}

inline int add(const int& a, const int& b) { return a+b; }
inline int div(const int& a, const int& b) { return a/b; }

int main() {
std::cout << fold_right<int,div>(100,10,5) // (100 / (10 / 5)) = 50
<< "n"
<< fold_left<int,div>(100,10,5) // Compiler error!
<< std::endl;
return 0;
}


How can variadic templates be used (in c++11) to correcty implement fold_left?



I think it essentially comes down to being able to "pop" the last argument off of a parameter pack, which I attempted to do in the left_fold template above, but as I said, this resulted in a compiler error.



Note: I have used simple arithmetic operations and integers as an example in this question, but the answer(s) should be generic enough to handle aggregation of objects using an arbitrary function (assuming it returns the same type of object as its arguments).



Note 2: For those familiar with c++17, fold expressions can be used to produce both left and right folds with binary operators. But these are not available in c++11.



As a related question: The above templates require the type T to be explicitly specified, as in fold_right<int,div>(...). Is there some way to formulate the template so that only the operation is required, e.g. fold_right<div>(...). I would think the type T could be inferred, but I don't see a way to order the template arguments to put the binary_op<> first.



Thanks!










share|improve this question





























    11















    I would like to use a c++ template to aggregate (fold) multiple arguments using a binary operation.



    Such a template could be used as follows:



    fold<add>(100,10,5) expands to add(add(100, 10), 5)



    The particular expansion shown above is the "left fold". The expansion add(100, add(10, 5)) is the "right fold". Assuming that the add function performs simple integer addition, both right and left folds produce the same result, 115.



    But consider a function div that performs integer division (div(a,b)=a/b). In this case, associativity matters and the left and right folds produce different results:



    fold_left<div>(100,10,5)  --> div(div(100, 10), 5) --> div(10, 5) -->  2
    fold_right<div>(100,10,5) --> div(100, div(10, 5)) --> div(100, 2) --> 50


    It is straightforward to use a variadic template to produce the right-associative version (fold_right), but I have not been able to figure out how to produce the left-associative version (fold_left). The attempted implementation of fold_left below results in a compiler error:



    #include <iostream>

    template <typename T> using binary_op = T(*)(const T& a, const T& b);

    // The terminal (single-argument) cases of the variadic functions defined later.
    template<typename T, binary_op<T> Operation> inline T fold_right(const T& t) { return t; }
    template<typename T, binary_op<T> Operation> inline T fold_left(const T& t) { return t; }

    // Combines arguments using right-associative operation
    // i.e. fold_right<T,op>(A,B,C) --> op(A, op(B,C))
    template<typename T, binary_op<T> Operation, typename ... Rest>
    inline T fold_right(const T& t, Rest... rest) {
    return Operation(t, fold_right<T, Operation>(rest...));
    }

    // Combines arguments using left-associative operation
    // i.e. fold_left<T,op>(A,B,C) --> op(op(A,B), C)
    template<typename T, binary_op<T> Operation, typename ... Rest>
    inline T fold_left(Rest... rest, const T& t) {
    return Operation(fold_left<T, Operation>(rest...), t);
    }

    inline int add(const int& a, const int& b) { return a+b; }
    inline int div(const int& a, const int& b) { return a/b; }

    int main() {
    std::cout << fold_right<int,div>(100,10,5) // (100 / (10 / 5)) = 50
    << "n"
    << fold_left<int,div>(100,10,5) // Compiler error!
    << std::endl;
    return 0;
    }


    How can variadic templates be used (in c++11) to correcty implement fold_left?



    I think it essentially comes down to being able to "pop" the last argument off of a parameter pack, which I attempted to do in the left_fold template above, but as I said, this resulted in a compiler error.



    Note: I have used simple arithmetic operations and integers as an example in this question, but the answer(s) should be generic enough to handle aggregation of objects using an arbitrary function (assuming it returns the same type of object as its arguments).



    Note 2: For those familiar with c++17, fold expressions can be used to produce both left and right folds with binary operators. But these are not available in c++11.



    As a related question: The above templates require the type T to be explicitly specified, as in fold_right<int,div>(...). Is there some way to formulate the template so that only the operation is required, e.g. fold_right<div>(...). I would think the type T could be inferred, but I don't see a way to order the template arguments to put the binary_op<> first.



    Thanks!










    share|improve this question



























      11












      11








      11


      0






      I would like to use a c++ template to aggregate (fold) multiple arguments using a binary operation.



      Such a template could be used as follows:



      fold<add>(100,10,5) expands to add(add(100, 10), 5)



      The particular expansion shown above is the "left fold". The expansion add(100, add(10, 5)) is the "right fold". Assuming that the add function performs simple integer addition, both right and left folds produce the same result, 115.



      But consider a function div that performs integer division (div(a,b)=a/b). In this case, associativity matters and the left and right folds produce different results:



      fold_left<div>(100,10,5)  --> div(div(100, 10), 5) --> div(10, 5) -->  2
      fold_right<div>(100,10,5) --> div(100, div(10, 5)) --> div(100, 2) --> 50


      It is straightforward to use a variadic template to produce the right-associative version (fold_right), but I have not been able to figure out how to produce the left-associative version (fold_left). The attempted implementation of fold_left below results in a compiler error:



      #include <iostream>

      template <typename T> using binary_op = T(*)(const T& a, const T& b);

      // The terminal (single-argument) cases of the variadic functions defined later.
      template<typename T, binary_op<T> Operation> inline T fold_right(const T& t) { return t; }
      template<typename T, binary_op<T> Operation> inline T fold_left(const T& t) { return t; }

      // Combines arguments using right-associative operation
      // i.e. fold_right<T,op>(A,B,C) --> op(A, op(B,C))
      template<typename T, binary_op<T> Operation, typename ... Rest>
      inline T fold_right(const T& t, Rest... rest) {
      return Operation(t, fold_right<T, Operation>(rest...));
      }

      // Combines arguments using left-associative operation
      // i.e. fold_left<T,op>(A,B,C) --> op(op(A,B), C)
      template<typename T, binary_op<T> Operation, typename ... Rest>
      inline T fold_left(Rest... rest, const T& t) {
      return Operation(fold_left<T, Operation>(rest...), t);
      }

      inline int add(const int& a, const int& b) { return a+b; }
      inline int div(const int& a, const int& b) { return a/b; }

      int main() {
      std::cout << fold_right<int,div>(100,10,5) // (100 / (10 / 5)) = 50
      << "n"
      << fold_left<int,div>(100,10,5) // Compiler error!
      << std::endl;
      return 0;
      }


      How can variadic templates be used (in c++11) to correcty implement fold_left?



      I think it essentially comes down to being able to "pop" the last argument off of a parameter pack, which I attempted to do in the left_fold template above, but as I said, this resulted in a compiler error.



      Note: I have used simple arithmetic operations and integers as an example in this question, but the answer(s) should be generic enough to handle aggregation of objects using an arbitrary function (assuming it returns the same type of object as its arguments).



      Note 2: For those familiar with c++17, fold expressions can be used to produce both left and right folds with binary operators. But these are not available in c++11.



      As a related question: The above templates require the type T to be explicitly specified, as in fold_right<int,div>(...). Is there some way to formulate the template so that only the operation is required, e.g. fold_right<div>(...). I would think the type T could be inferred, but I don't see a way to order the template arguments to put the binary_op<> first.



      Thanks!










      share|improve this question
















      I would like to use a c++ template to aggregate (fold) multiple arguments using a binary operation.



      Such a template could be used as follows:



      fold<add>(100,10,5) expands to add(add(100, 10), 5)



      The particular expansion shown above is the "left fold". The expansion add(100, add(10, 5)) is the "right fold". Assuming that the add function performs simple integer addition, both right and left folds produce the same result, 115.



      But consider a function div that performs integer division (div(a,b)=a/b). In this case, associativity matters and the left and right folds produce different results:



      fold_left<div>(100,10,5)  --> div(div(100, 10), 5) --> div(10, 5) -->  2
      fold_right<div>(100,10,5) --> div(100, div(10, 5)) --> div(100, 2) --> 50


      It is straightforward to use a variadic template to produce the right-associative version (fold_right), but I have not been able to figure out how to produce the left-associative version (fold_left). The attempted implementation of fold_left below results in a compiler error:



      #include <iostream>

      template <typename T> using binary_op = T(*)(const T& a, const T& b);

      // The terminal (single-argument) cases of the variadic functions defined later.
      template<typename T, binary_op<T> Operation> inline T fold_right(const T& t) { return t; }
      template<typename T, binary_op<T> Operation> inline T fold_left(const T& t) { return t; }

      // Combines arguments using right-associative operation
      // i.e. fold_right<T,op>(A,B,C) --> op(A, op(B,C))
      template<typename T, binary_op<T> Operation, typename ... Rest>
      inline T fold_right(const T& t, Rest... rest) {
      return Operation(t, fold_right<T, Operation>(rest...));
      }

      // Combines arguments using left-associative operation
      // i.e. fold_left<T,op>(A,B,C) --> op(op(A,B), C)
      template<typename T, binary_op<T> Operation, typename ... Rest>
      inline T fold_left(Rest... rest, const T& t) {
      return Operation(fold_left<T, Operation>(rest...), t);
      }

      inline int add(const int& a, const int& b) { return a+b; }
      inline int div(const int& a, const int& b) { return a/b; }

      int main() {
      std::cout << fold_right<int,div>(100,10,5) // (100 / (10 / 5)) = 50
      << "n"
      << fold_left<int,div>(100,10,5) // Compiler error!
      << std::endl;
      return 0;
      }


      How can variadic templates be used (in c++11) to correcty implement fold_left?



      I think it essentially comes down to being able to "pop" the last argument off of a parameter pack, which I attempted to do in the left_fold template above, but as I said, this resulted in a compiler error.



      Note: I have used simple arithmetic operations and integers as an example in this question, but the answer(s) should be generic enough to handle aggregation of objects using an arbitrary function (assuming it returns the same type of object as its arguments).



      Note 2: For those familiar with c++17, fold expressions can be used to produce both left and right folds with binary operators. But these are not available in c++11.



      As a related question: The above templates require the type T to be explicitly specified, as in fold_right<int,div>(...). Is there some way to formulate the template so that only the operation is required, e.g. fold_right<div>(...). I would think the type T could be inferred, but I don't see a way to order the template arguments to put the binary_op<> first.



      Thanks!







      c++ c++11 templates variadic-templates fold






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Feb 23 at 7:36









      xskxzr

      7,03982557




      7,03982557










      asked Feb 19 at 6:59









      drwatsoncodedrwatsoncode

      2,1231830




      2,1231830
























          3 Answers
          3






          active

          oldest

          votes


















          10














          Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:



          template<typename T, binary_op<T> Operation> 
          inline T fold_left(const T& t) { return t; }

          template<typename T, binary_op<T> Operation, typename ... Rest>
          inline T fold_left(const T& a, const T& b, Rest... rest) {
          return fold_left<T, Operation>(Operation(a,b), rest...);
          }





          share|improve this answer



















          • 1





            It compiles and seems to work. Live Demo on coliru

            – Scheff
            Feb 19 at 7:46





















          2














          Michael answered your 1st Q.
          The 2nd may have different answers. My prefered way is to define your operations as functors with template members:



          #include <type_traits>
          struct homogene_add{
          template<typename T>
          T operator()(T const& lhs, T const& rhs){/*...*/}
          };

          struct mixed_add{
          template<typename L, typename R>
          std::common_type<L,R>::type
          operator()(L const& lhs, R const& rhs){/*...*/}
          };

          template<typename binary_op, typename ... Args>
          std::common_type<Args...>::type
          fold_right(const Args&... args);

          template<typename binary_op, typename First, typename ... Args>
          std::common_type<First, Args...>::type
          fold_right(const First& init, const Args&... args) {
          binary_op op;
          return op(init, fold_right<binary_op>(args...));
          };

          template<typename binary_op, typename First>
          const First& fold_right(const First& init) {
          return init;
          };


          CV qualification and valueness correctness, I leave to the OP.






          share|improve this answer


























          • Thanks for the suggestion. Unfortunately auto cannot be used as a return type in c++11 without using trailing return type syntax (e.g. auto f() -> decltype(...)). Would it be possible to change your definitions of fold_right to be compatible with c++11 ?

            – drwatsoncode
            Feb 19 at 18:36











          • I'd like to use this approach, but even when compiling with c++14, your forward declaration of auto fold_right(const Args&... args); seems to cause a compiler error: error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’

            – drwatsoncode
            Feb 19 at 18:52













          • @ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.

            – Red.Wave
            Feb 19 at 18:56











          • @ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.

            – Red.Wave
            Feb 19 at 19:18











          • The problem I see with your updated version is that std::common_type might be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices. scalar*Matrix multiplication is well-defined and produces a Matrix. Also Matrix*Matrix is well defined and produces another Matrix, so the expression Matrix M3 = s1*M1*M2; (where s1 is a scalar and M's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and cause fold_right to fail).

            – drwatsoncode
            Feb 19 at 20:41





















          0














          That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.



          That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.






          share|improve this answer



















          • 1





            Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?

            – drwatsoncode
            Feb 19 at 7:30













          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54760418%2fhow-can-a-variadic-template-be-used-to-generate-a-left-associative-expression-a%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          3 Answers
          3






          active

          oldest

          votes








          3 Answers
          3






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          10














          Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:



          template<typename T, binary_op<T> Operation> 
          inline T fold_left(const T& t) { return t; }

          template<typename T, binary_op<T> Operation, typename ... Rest>
          inline T fold_left(const T& a, const T& b, Rest... rest) {
          return fold_left<T, Operation>(Operation(a,b), rest...);
          }





          share|improve this answer



















          • 1





            It compiles and seems to work. Live Demo on coliru

            – Scheff
            Feb 19 at 7:46


















          10














          Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:



          template<typename T, binary_op<T> Operation> 
          inline T fold_left(const T& t) { return t; }

          template<typename T, binary_op<T> Operation, typename ... Rest>
          inline T fold_left(const T& a, const T& b, Rest... rest) {
          return fold_left<T, Operation>(Operation(a,b), rest...);
          }





          share|improve this answer



















          • 1





            It compiles and seems to work. Live Demo on coliru

            – Scheff
            Feb 19 at 7:46
















          10












          10








          10







          Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:



          template<typename T, binary_op<T> Operation> 
          inline T fold_left(const T& t) { return t; }

          template<typename T, binary_op<T> Operation, typename ... Rest>
          inline T fold_left(const T& a, const T& b, Rest... rest) {
          return fold_left<T, Operation>(Operation(a,b), rest...);
          }





          share|improve this answer













          Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:



          template<typename T, binary_op<T> Operation> 
          inline T fold_left(const T& t) { return t; }

          template<typename T, binary_op<T> Operation, typename ... Rest>
          inline T fold_left(const T& a, const T& b, Rest... rest) {
          return fold_left<T, Operation>(Operation(a,b), rest...);
          }






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Feb 19 at 7:40









          Michael VekslerMichael Veksler

          4,9271624




          4,9271624








          • 1





            It compiles and seems to work. Live Demo on coliru

            – Scheff
            Feb 19 at 7:46
















          • 1





            It compiles and seems to work. Live Demo on coliru

            – Scheff
            Feb 19 at 7:46










          1




          1





          It compiles and seems to work. Live Demo on coliru

          – Scheff
          Feb 19 at 7:46







          It compiles and seems to work. Live Demo on coliru

          – Scheff
          Feb 19 at 7:46















          2














          Michael answered your 1st Q.
          The 2nd may have different answers. My prefered way is to define your operations as functors with template members:



          #include <type_traits>
          struct homogene_add{
          template<typename T>
          T operator()(T const& lhs, T const& rhs){/*...*/}
          };

          struct mixed_add{
          template<typename L, typename R>
          std::common_type<L,R>::type
          operator()(L const& lhs, R const& rhs){/*...*/}
          };

          template<typename binary_op, typename ... Args>
          std::common_type<Args...>::type
          fold_right(const Args&... args);

          template<typename binary_op, typename First, typename ... Args>
          std::common_type<First, Args...>::type
          fold_right(const First& init, const Args&... args) {
          binary_op op;
          return op(init, fold_right<binary_op>(args...));
          };

          template<typename binary_op, typename First>
          const First& fold_right(const First& init) {
          return init;
          };


          CV qualification and valueness correctness, I leave to the OP.






          share|improve this answer


























          • Thanks for the suggestion. Unfortunately auto cannot be used as a return type in c++11 without using trailing return type syntax (e.g. auto f() -> decltype(...)). Would it be possible to change your definitions of fold_right to be compatible with c++11 ?

            – drwatsoncode
            Feb 19 at 18:36











          • I'd like to use this approach, but even when compiling with c++14, your forward declaration of auto fold_right(const Args&... args); seems to cause a compiler error: error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’

            – drwatsoncode
            Feb 19 at 18:52













          • @ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.

            – Red.Wave
            Feb 19 at 18:56











          • @ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.

            – Red.Wave
            Feb 19 at 19:18











          • The problem I see with your updated version is that std::common_type might be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices. scalar*Matrix multiplication is well-defined and produces a Matrix. Also Matrix*Matrix is well defined and produces another Matrix, so the expression Matrix M3 = s1*M1*M2; (where s1 is a scalar and M's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and cause fold_right to fail).

            – drwatsoncode
            Feb 19 at 20:41


















          2














          Michael answered your 1st Q.
          The 2nd may have different answers. My prefered way is to define your operations as functors with template members:



          #include <type_traits>
          struct homogene_add{
          template<typename T>
          T operator()(T const& lhs, T const& rhs){/*...*/}
          };

          struct mixed_add{
          template<typename L, typename R>
          std::common_type<L,R>::type
          operator()(L const& lhs, R const& rhs){/*...*/}
          };

          template<typename binary_op, typename ... Args>
          std::common_type<Args...>::type
          fold_right(const Args&... args);

          template<typename binary_op, typename First, typename ... Args>
          std::common_type<First, Args...>::type
          fold_right(const First& init, const Args&... args) {
          binary_op op;
          return op(init, fold_right<binary_op>(args...));
          };

          template<typename binary_op, typename First>
          const First& fold_right(const First& init) {
          return init;
          };


          CV qualification and valueness correctness, I leave to the OP.






          share|improve this answer


























          • Thanks for the suggestion. Unfortunately auto cannot be used as a return type in c++11 without using trailing return type syntax (e.g. auto f() -> decltype(...)). Would it be possible to change your definitions of fold_right to be compatible with c++11 ?

            – drwatsoncode
            Feb 19 at 18:36











          • I'd like to use this approach, but even when compiling with c++14, your forward declaration of auto fold_right(const Args&... args); seems to cause a compiler error: error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’

            – drwatsoncode
            Feb 19 at 18:52













          • @ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.

            – Red.Wave
            Feb 19 at 18:56











          • @ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.

            – Red.Wave
            Feb 19 at 19:18











          • The problem I see with your updated version is that std::common_type might be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices. scalar*Matrix multiplication is well-defined and produces a Matrix. Also Matrix*Matrix is well defined and produces another Matrix, so the expression Matrix M3 = s1*M1*M2; (where s1 is a scalar and M's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and cause fold_right to fail).

            – drwatsoncode
            Feb 19 at 20:41
















          2












          2








          2







          Michael answered your 1st Q.
          The 2nd may have different answers. My prefered way is to define your operations as functors with template members:



          #include <type_traits>
          struct homogene_add{
          template<typename T>
          T operator()(T const& lhs, T const& rhs){/*...*/}
          };

          struct mixed_add{
          template<typename L, typename R>
          std::common_type<L,R>::type
          operator()(L const& lhs, R const& rhs){/*...*/}
          };

          template<typename binary_op, typename ... Args>
          std::common_type<Args...>::type
          fold_right(const Args&... args);

          template<typename binary_op, typename First, typename ... Args>
          std::common_type<First, Args...>::type
          fold_right(const First& init, const Args&... args) {
          binary_op op;
          return op(init, fold_right<binary_op>(args...));
          };

          template<typename binary_op, typename First>
          const First& fold_right(const First& init) {
          return init;
          };


          CV qualification and valueness correctness, I leave to the OP.






          share|improve this answer















          Michael answered your 1st Q.
          The 2nd may have different answers. My prefered way is to define your operations as functors with template members:



          #include <type_traits>
          struct homogene_add{
          template<typename T>
          T operator()(T const& lhs, T const& rhs){/*...*/}
          };

          struct mixed_add{
          template<typename L, typename R>
          std::common_type<L,R>::type
          operator()(L const& lhs, R const& rhs){/*...*/}
          };

          template<typename binary_op, typename ... Args>
          std::common_type<Args...>::type
          fold_right(const Args&... args);

          template<typename binary_op, typename First, typename ... Args>
          std::common_type<First, Args...>::type
          fold_right(const First& init, const Args&... args) {
          binary_op op;
          return op(init, fold_right<binary_op>(args...));
          };

          template<typename binary_op, typename First>
          const First& fold_right(const First& init) {
          return init;
          };


          CV qualification and valueness correctness, I leave to the OP.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Feb 22 at 22:39









          Michael Veksler

          4,9271624




          4,9271624










          answered Feb 19 at 8:35









          Red.WaveRed.Wave

          87037




          87037













          • Thanks for the suggestion. Unfortunately auto cannot be used as a return type in c++11 without using trailing return type syntax (e.g. auto f() -> decltype(...)). Would it be possible to change your definitions of fold_right to be compatible with c++11 ?

            – drwatsoncode
            Feb 19 at 18:36











          • I'd like to use this approach, but even when compiling with c++14, your forward declaration of auto fold_right(const Args&... args); seems to cause a compiler error: error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’

            – drwatsoncode
            Feb 19 at 18:52













          • @ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.

            – Red.Wave
            Feb 19 at 18:56











          • @ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.

            – Red.Wave
            Feb 19 at 19:18











          • The problem I see with your updated version is that std::common_type might be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices. scalar*Matrix multiplication is well-defined and produces a Matrix. Also Matrix*Matrix is well defined and produces another Matrix, so the expression Matrix M3 = s1*M1*M2; (where s1 is a scalar and M's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and cause fold_right to fail).

            – drwatsoncode
            Feb 19 at 20:41





















          • Thanks for the suggestion. Unfortunately auto cannot be used as a return type in c++11 without using trailing return type syntax (e.g. auto f() -> decltype(...)). Would it be possible to change your definitions of fold_right to be compatible with c++11 ?

            – drwatsoncode
            Feb 19 at 18:36











          • I'd like to use this approach, but even when compiling with c++14, your forward declaration of auto fold_right(const Args&... args); seems to cause a compiler error: error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’

            – drwatsoncode
            Feb 19 at 18:52













          • @ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.

            – Red.Wave
            Feb 19 at 18:56











          • @ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.

            – Red.Wave
            Feb 19 at 19:18











          • The problem I see with your updated version is that std::common_type might be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices. scalar*Matrix multiplication is well-defined and produces a Matrix. Also Matrix*Matrix is well defined and produces another Matrix, so the expression Matrix M3 = s1*M1*M2; (where s1 is a scalar and M's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and cause fold_right to fail).

            – drwatsoncode
            Feb 19 at 20:41



















          Thanks for the suggestion. Unfortunately auto cannot be used as a return type in c++11 without using trailing return type syntax (e.g. auto f() -> decltype(...)). Would it be possible to change your definitions of fold_right to be compatible with c++11 ?

          – drwatsoncode
          Feb 19 at 18:36





          Thanks for the suggestion. Unfortunately auto cannot be used as a return type in c++11 without using trailing return type syntax (e.g. auto f() -> decltype(...)). Would it be possible to change your definitions of fold_right to be compatible with c++11 ?

          – drwatsoncode
          Feb 19 at 18:36













          I'd like to use this approach, but even when compiling with c++14, your forward declaration of auto fold_right(const Args&... args); seems to cause a compiler error: error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’

          – drwatsoncode
          Feb 19 at 18:52







          I'd like to use this approach, but even when compiling with c++14, your forward declaration of auto fold_right(const Args&... args); seems to cause a compiler error: error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’

          – drwatsoncode
          Feb 19 at 18:52















          @ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.

          – Red.Wave
          Feb 19 at 18:56





          @ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.

          – Red.Wave
          Feb 19 at 18:56













          @ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.

          – Red.Wave
          Feb 19 at 19:18





          @ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.

          – Red.Wave
          Feb 19 at 19:18













          The problem I see with your updated version is that std::common_type might be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices. scalar*Matrix multiplication is well-defined and produces a Matrix. Also Matrix*Matrix is well defined and produces another Matrix, so the expression Matrix M3 = s1*M1*M2; (where s1 is a scalar and M's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and cause fold_right to fail).

          – drwatsoncode
          Feb 19 at 20:41







          The problem I see with your updated version is that std::common_type might be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices. scalar*Matrix multiplication is well-defined and produces a Matrix. Also Matrix*Matrix is well defined and produces another Matrix, so the expression Matrix M3 = s1*M1*M2; (where s1 is a scalar and M's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and cause fold_right to fail).

          – drwatsoncode
          Feb 19 at 20:41













          0














          That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.



          That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.






          share|improve this answer



















          • 1





            Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?

            – drwatsoncode
            Feb 19 at 7:30


















          0














          That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.



          That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.






          share|improve this answer



















          • 1





            Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?

            – drwatsoncode
            Feb 19 at 7:30
















          0












          0








          0







          That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.



          That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.






          share|improve this answer













          That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.



          That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Feb 19 at 7:20









          AcornAcorn

          5,84211339




          5,84211339








          • 1





            Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?

            – drwatsoncode
            Feb 19 at 7:30
















          • 1





            Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?

            – drwatsoncode
            Feb 19 at 7:30










          1




          1





          Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?

          – drwatsoncode
          Feb 19 at 7:30







          Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?

          – drwatsoncode
          Feb 19 at 7:30




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54760418%2fhow-can-a-variadic-template-be-used-to-generate-a-left-associative-expression-a%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Index of /

          Tribalistas

          Listed building