Why is `const T&` not sure to be const?












40















template<typename T>
void f(T a, const T& b)
{
++a; // ok
++b; // also ok!
}

template<typename T>
void g(T n)
{
f<T>(n, n);
}

int main()
{
int n{};
g<int&>(n);
}


Please note: b is of const T& and ++b is ok!



Why is const T& not sure to be const?










share|improve this question





























    40















    template<typename T>
    void f(T a, const T& b)
    {
    ++a; // ok
    ++b; // also ok!
    }

    template<typename T>
    void g(T n)
    {
    f<T>(n, n);
    }

    int main()
    {
    int n{};
    g<int&>(n);
    }


    Please note: b is of const T& and ++b is ok!



    Why is const T& not sure to be const?










    share|improve this question



























      40












      40








      40


      9






      template<typename T>
      void f(T a, const T& b)
      {
      ++a; // ok
      ++b; // also ok!
      }

      template<typename T>
      void g(T n)
      {
      f<T>(n, n);
      }

      int main()
      {
      int n{};
      g<int&>(n);
      }


      Please note: b is of const T& and ++b is ok!



      Why is const T& not sure to be const?










      share|improve this question
















      template<typename T>
      void f(T a, const T& b)
      {
      ++a; // ok
      ++b; // also ok!
      }

      template<typename T>
      void g(T n)
      {
      f<T>(n, n);
      }

      int main()
      {
      int n{};
      g<int&>(n);
      }


      Please note: b is of const T& and ++b is ok!



      Why is const T& not sure to be const?







      c++ c++11 const function-templates const-reference






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 22 hours ago









      curiousguy

      4,54523044




      4,54523044










      asked yesterday









      xmllmxxmllmx

      13.7k984210




      13.7k984210
























          2 Answers
          2






          active

          oldest

          votes


















          42














          Welcome to const and reference collapsing. When you have const T&, the reference gets applied to T, and so does the const. You call g like



          g<int&>(n);


          so you have specified that T is a int&. When we apply a reference to an lvalue reference, the two references collapse to a single one, so int& & becomes just int&. Then we get to the rule from [dcl.ref]/1, which states that if you apply const to a reference it is discarded, so int& const just becomes int& (note that you can't actually declare int& const, it has to come from a typedef or template). That means for



          g<int&>(n);


          you are actually calling



          void f(int& a, int& b)


          and you are not actually modifying a constant.





          Had you called g as



          g<int>(n);
          // or just
          g(n);


          then T would be int, and f would have been stamped out as



          void f(int a, const int& b)


          Since T isn't a reference anymore, the const and the & get applied to it, and you would have received a compiler error for trying to modify a constant variable.






          share|improve this answer





















          • 4





            This is why the type traits like std::add_lvalue_reference exist, to ensure that references are added in a predictable way to prevent just this sort of pain.

            – Mgetz
            yesterday






          • 5





            One way to make it easier to understand is to write T const& instead of const T& (which is the same), and then replace T with int&.

            – Ruslan
            yesterday













          • I would reorder some of the first part of this answer, since in the type const T&, first the const applies to T, and then the lvalue-reference applies to the result of that. (If the rule were the opposite, the "const applied to a reference type is ignored" rule would always kick in, and const T& would always mean the same as T&.)

            – aschepler
            yesterday











          • @aschepler The rules stop T& const, not const T&/T const &

            – NathanOliver
            yesterday











          • @NathanOliver I'm just suggesting discussing the effect of const before the effect of lvalue-reference, since using U = const T&; means the same as using tmp1 = const T; using U = tmp1&; but not the same as using tmp2 = T&; using U = const tmp2;

            – aschepler
            yesterday



















          -1














          I know that there is already an accepted answer which is correct but just to add to it a little bit, even outside the realm of templates and just in function declarations in general...



          ( const T& ) 


          is not the same as



          ( const T )


          In your example which matches the first, you have a const reference. If you truly want a const value that is not modifiable remove the reference as in the second example.






          share|improve this answer



















          • 8





            when T is int&, const T& and const T both give int&.

            – Ben Voigt
            yesterday











          • I think there was a misconception on what I was trying to say; I'm using T here not as a template parameter. T was just meant to be used as any data type: int, float, double etc.. so T in my example above should never be int&. I did specifically state outside the realm of templates.

            – Francis Cugler
            yesterday











          • Hmm, it sounds as if you are explaining that you can see this without templates, which is no problem. But then your final sentence claims to offer a solution to OP's problem, and that problem definitely involves templates. It's fine to offer a solution to a template question which is broader than just templates. But a solution to a template question that isn't accurate for templates, seems not to answer the question.

            – Ben Voigt
            yesterday













          • This can also be an issue with no templates involved: using T = int&; void f(const T&); declares void f(int&);.

            – aschepler
            yesterday











          • @aschepler true, but I wasn't referring to using clauses; just basic function declarations in general.

            – Francis Cugler
            yesterday











          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%2f54480599%2fwhy-is-const-t-not-sure-to-be-const%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          42














          Welcome to const and reference collapsing. When you have const T&, the reference gets applied to T, and so does the const. You call g like



          g<int&>(n);


          so you have specified that T is a int&. When we apply a reference to an lvalue reference, the two references collapse to a single one, so int& & becomes just int&. Then we get to the rule from [dcl.ref]/1, which states that if you apply const to a reference it is discarded, so int& const just becomes int& (note that you can't actually declare int& const, it has to come from a typedef or template). That means for



          g<int&>(n);


          you are actually calling



          void f(int& a, int& b)


          and you are not actually modifying a constant.





          Had you called g as



          g<int>(n);
          // or just
          g(n);


          then T would be int, and f would have been stamped out as



          void f(int a, const int& b)


          Since T isn't a reference anymore, the const and the & get applied to it, and you would have received a compiler error for trying to modify a constant variable.






          share|improve this answer





















          • 4





            This is why the type traits like std::add_lvalue_reference exist, to ensure that references are added in a predictable way to prevent just this sort of pain.

            – Mgetz
            yesterday






          • 5





            One way to make it easier to understand is to write T const& instead of const T& (which is the same), and then replace T with int&.

            – Ruslan
            yesterday













          • I would reorder some of the first part of this answer, since in the type const T&, first the const applies to T, and then the lvalue-reference applies to the result of that. (If the rule were the opposite, the "const applied to a reference type is ignored" rule would always kick in, and const T& would always mean the same as T&.)

            – aschepler
            yesterday











          • @aschepler The rules stop T& const, not const T&/T const &

            – NathanOliver
            yesterday











          • @NathanOliver I'm just suggesting discussing the effect of const before the effect of lvalue-reference, since using U = const T&; means the same as using tmp1 = const T; using U = tmp1&; but not the same as using tmp2 = T&; using U = const tmp2;

            – aschepler
            yesterday
















          42














          Welcome to const and reference collapsing. When you have const T&, the reference gets applied to T, and so does the const. You call g like



          g<int&>(n);


          so you have specified that T is a int&. When we apply a reference to an lvalue reference, the two references collapse to a single one, so int& & becomes just int&. Then we get to the rule from [dcl.ref]/1, which states that if you apply const to a reference it is discarded, so int& const just becomes int& (note that you can't actually declare int& const, it has to come from a typedef or template). That means for



          g<int&>(n);


          you are actually calling



          void f(int& a, int& b)


          and you are not actually modifying a constant.





          Had you called g as



          g<int>(n);
          // or just
          g(n);


          then T would be int, and f would have been stamped out as



          void f(int a, const int& b)


          Since T isn't a reference anymore, the const and the & get applied to it, and you would have received a compiler error for trying to modify a constant variable.






          share|improve this answer





















          • 4





            This is why the type traits like std::add_lvalue_reference exist, to ensure that references are added in a predictable way to prevent just this sort of pain.

            – Mgetz
            yesterday






          • 5





            One way to make it easier to understand is to write T const& instead of const T& (which is the same), and then replace T with int&.

            – Ruslan
            yesterday













          • I would reorder some of the first part of this answer, since in the type const T&, first the const applies to T, and then the lvalue-reference applies to the result of that. (If the rule were the opposite, the "const applied to a reference type is ignored" rule would always kick in, and const T& would always mean the same as T&.)

            – aschepler
            yesterday











          • @aschepler The rules stop T& const, not const T&/T const &

            – NathanOliver
            yesterday











          • @NathanOliver I'm just suggesting discussing the effect of const before the effect of lvalue-reference, since using U = const T&; means the same as using tmp1 = const T; using U = tmp1&; but not the same as using tmp2 = T&; using U = const tmp2;

            – aschepler
            yesterday














          42












          42








          42







          Welcome to const and reference collapsing. When you have const T&, the reference gets applied to T, and so does the const. You call g like



          g<int&>(n);


          so you have specified that T is a int&. When we apply a reference to an lvalue reference, the two references collapse to a single one, so int& & becomes just int&. Then we get to the rule from [dcl.ref]/1, which states that if you apply const to a reference it is discarded, so int& const just becomes int& (note that you can't actually declare int& const, it has to come from a typedef or template). That means for



          g<int&>(n);


          you are actually calling



          void f(int& a, int& b)


          and you are not actually modifying a constant.





          Had you called g as



          g<int>(n);
          // or just
          g(n);


          then T would be int, and f would have been stamped out as



          void f(int a, const int& b)


          Since T isn't a reference anymore, the const and the & get applied to it, and you would have received a compiler error for trying to modify a constant variable.






          share|improve this answer















          Welcome to const and reference collapsing. When you have const T&, the reference gets applied to T, and so does the const. You call g like



          g<int&>(n);


          so you have specified that T is a int&. When we apply a reference to an lvalue reference, the two references collapse to a single one, so int& & becomes just int&. Then we get to the rule from [dcl.ref]/1, which states that if you apply const to a reference it is discarded, so int& const just becomes int& (note that you can't actually declare int& const, it has to come from a typedef or template). That means for



          g<int&>(n);


          you are actually calling



          void f(int& a, int& b)


          and you are not actually modifying a constant.





          Had you called g as



          g<int>(n);
          // or just
          g(n);


          then T would be int, and f would have been stamped out as



          void f(int a, const int& b)


          Since T isn't a reference anymore, the const and the & get applied to it, and you would have received a compiler error for trying to modify a constant variable.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 22 hours ago









          Fabio Turati

          2,59152238




          2,59152238










          answered yesterday









          NathanOliverNathanOliver

          91.2k15129193




          91.2k15129193








          • 4





            This is why the type traits like std::add_lvalue_reference exist, to ensure that references are added in a predictable way to prevent just this sort of pain.

            – Mgetz
            yesterday






          • 5





            One way to make it easier to understand is to write T const& instead of const T& (which is the same), and then replace T with int&.

            – Ruslan
            yesterday













          • I would reorder some of the first part of this answer, since in the type const T&, first the const applies to T, and then the lvalue-reference applies to the result of that. (If the rule were the opposite, the "const applied to a reference type is ignored" rule would always kick in, and const T& would always mean the same as T&.)

            – aschepler
            yesterday











          • @aschepler The rules stop T& const, not const T&/T const &

            – NathanOliver
            yesterday











          • @NathanOliver I'm just suggesting discussing the effect of const before the effect of lvalue-reference, since using U = const T&; means the same as using tmp1 = const T; using U = tmp1&; but not the same as using tmp2 = T&; using U = const tmp2;

            – aschepler
            yesterday














          • 4





            This is why the type traits like std::add_lvalue_reference exist, to ensure that references are added in a predictable way to prevent just this sort of pain.

            – Mgetz
            yesterday






          • 5





            One way to make it easier to understand is to write T const& instead of const T& (which is the same), and then replace T with int&.

            – Ruslan
            yesterday













          • I would reorder some of the first part of this answer, since in the type const T&, first the const applies to T, and then the lvalue-reference applies to the result of that. (If the rule were the opposite, the "const applied to a reference type is ignored" rule would always kick in, and const T& would always mean the same as T&.)

            – aschepler
            yesterday











          • @aschepler The rules stop T& const, not const T&/T const &

            – NathanOliver
            yesterday











          • @NathanOliver I'm just suggesting discussing the effect of const before the effect of lvalue-reference, since using U = const T&; means the same as using tmp1 = const T; using U = tmp1&; but not the same as using tmp2 = T&; using U = const tmp2;

            – aschepler
            yesterday








          4




          4





          This is why the type traits like std::add_lvalue_reference exist, to ensure that references are added in a predictable way to prevent just this sort of pain.

          – Mgetz
          yesterday





          This is why the type traits like std::add_lvalue_reference exist, to ensure that references are added in a predictable way to prevent just this sort of pain.

          – Mgetz
          yesterday




          5




          5





          One way to make it easier to understand is to write T const& instead of const T& (which is the same), and then replace T with int&.

          – Ruslan
          yesterday







          One way to make it easier to understand is to write T const& instead of const T& (which is the same), and then replace T with int&.

          – Ruslan
          yesterday















          I would reorder some of the first part of this answer, since in the type const T&, first the const applies to T, and then the lvalue-reference applies to the result of that. (If the rule were the opposite, the "const applied to a reference type is ignored" rule would always kick in, and const T& would always mean the same as T&.)

          – aschepler
          yesterday





          I would reorder some of the first part of this answer, since in the type const T&, first the const applies to T, and then the lvalue-reference applies to the result of that. (If the rule were the opposite, the "const applied to a reference type is ignored" rule would always kick in, and const T& would always mean the same as T&.)

          – aschepler
          yesterday













          @aschepler The rules stop T& const, not const T&/T const &

          – NathanOliver
          yesterday





          @aschepler The rules stop T& const, not const T&/T const &

          – NathanOliver
          yesterday













          @NathanOliver I'm just suggesting discussing the effect of const before the effect of lvalue-reference, since using U = const T&; means the same as using tmp1 = const T; using U = tmp1&; but not the same as using tmp2 = T&; using U = const tmp2;

          – aschepler
          yesterday





          @NathanOliver I'm just suggesting discussing the effect of const before the effect of lvalue-reference, since using U = const T&; means the same as using tmp1 = const T; using U = tmp1&; but not the same as using tmp2 = T&; using U = const tmp2;

          – aschepler
          yesterday













          -1














          I know that there is already an accepted answer which is correct but just to add to it a little bit, even outside the realm of templates and just in function declarations in general...



          ( const T& ) 


          is not the same as



          ( const T )


          In your example which matches the first, you have a const reference. If you truly want a const value that is not modifiable remove the reference as in the second example.






          share|improve this answer



















          • 8





            when T is int&, const T& and const T both give int&.

            – Ben Voigt
            yesterday











          • I think there was a misconception on what I was trying to say; I'm using T here not as a template parameter. T was just meant to be used as any data type: int, float, double etc.. so T in my example above should never be int&. I did specifically state outside the realm of templates.

            – Francis Cugler
            yesterday











          • Hmm, it sounds as if you are explaining that you can see this without templates, which is no problem. But then your final sentence claims to offer a solution to OP's problem, and that problem definitely involves templates. It's fine to offer a solution to a template question which is broader than just templates. But a solution to a template question that isn't accurate for templates, seems not to answer the question.

            – Ben Voigt
            yesterday













          • This can also be an issue with no templates involved: using T = int&; void f(const T&); declares void f(int&);.

            – aschepler
            yesterday











          • @aschepler true, but I wasn't referring to using clauses; just basic function declarations in general.

            – Francis Cugler
            yesterday
















          -1














          I know that there is already an accepted answer which is correct but just to add to it a little bit, even outside the realm of templates and just in function declarations in general...



          ( const T& ) 


          is not the same as



          ( const T )


          In your example which matches the first, you have a const reference. If you truly want a const value that is not modifiable remove the reference as in the second example.






          share|improve this answer



















          • 8





            when T is int&, const T& and const T both give int&.

            – Ben Voigt
            yesterday











          • I think there was a misconception on what I was trying to say; I'm using T here not as a template parameter. T was just meant to be used as any data type: int, float, double etc.. so T in my example above should never be int&. I did specifically state outside the realm of templates.

            – Francis Cugler
            yesterday











          • Hmm, it sounds as if you are explaining that you can see this without templates, which is no problem. But then your final sentence claims to offer a solution to OP's problem, and that problem definitely involves templates. It's fine to offer a solution to a template question which is broader than just templates. But a solution to a template question that isn't accurate for templates, seems not to answer the question.

            – Ben Voigt
            yesterday













          • This can also be an issue with no templates involved: using T = int&; void f(const T&); declares void f(int&);.

            – aschepler
            yesterday











          • @aschepler true, but I wasn't referring to using clauses; just basic function declarations in general.

            – Francis Cugler
            yesterday














          -1












          -1








          -1







          I know that there is already an accepted answer which is correct but just to add to it a little bit, even outside the realm of templates and just in function declarations in general...



          ( const T& ) 


          is not the same as



          ( const T )


          In your example which matches the first, you have a const reference. If you truly want a const value that is not modifiable remove the reference as in the second example.






          share|improve this answer













          I know that there is already an accepted answer which is correct but just to add to it a little bit, even outside the realm of templates and just in function declarations in general...



          ( const T& ) 


          is not the same as



          ( const T )


          In your example which matches the first, you have a const reference. If you truly want a const value that is not modifiable remove the reference as in the second example.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered yesterday









          Francis CuglerFrancis Cugler

          4,58311227




          4,58311227








          • 8





            when T is int&, const T& and const T both give int&.

            – Ben Voigt
            yesterday











          • I think there was a misconception on what I was trying to say; I'm using T here not as a template parameter. T was just meant to be used as any data type: int, float, double etc.. so T in my example above should never be int&. I did specifically state outside the realm of templates.

            – Francis Cugler
            yesterday











          • Hmm, it sounds as if you are explaining that you can see this without templates, which is no problem. But then your final sentence claims to offer a solution to OP's problem, and that problem definitely involves templates. It's fine to offer a solution to a template question which is broader than just templates. But a solution to a template question that isn't accurate for templates, seems not to answer the question.

            – Ben Voigt
            yesterday













          • This can also be an issue with no templates involved: using T = int&; void f(const T&); declares void f(int&);.

            – aschepler
            yesterday











          • @aschepler true, but I wasn't referring to using clauses; just basic function declarations in general.

            – Francis Cugler
            yesterday














          • 8





            when T is int&, const T& and const T both give int&.

            – Ben Voigt
            yesterday











          • I think there was a misconception on what I was trying to say; I'm using T here not as a template parameter. T was just meant to be used as any data type: int, float, double etc.. so T in my example above should never be int&. I did specifically state outside the realm of templates.

            – Francis Cugler
            yesterday











          • Hmm, it sounds as if you are explaining that you can see this without templates, which is no problem. But then your final sentence claims to offer a solution to OP's problem, and that problem definitely involves templates. It's fine to offer a solution to a template question which is broader than just templates. But a solution to a template question that isn't accurate for templates, seems not to answer the question.

            – Ben Voigt
            yesterday













          • This can also be an issue with no templates involved: using T = int&; void f(const T&); declares void f(int&);.

            – aschepler
            yesterday











          • @aschepler true, but I wasn't referring to using clauses; just basic function declarations in general.

            – Francis Cugler
            yesterday








          8




          8





          when T is int&, const T& and const T both give int&.

          – Ben Voigt
          yesterday





          when T is int&, const T& and const T both give int&.

          – Ben Voigt
          yesterday













          I think there was a misconception on what I was trying to say; I'm using T here not as a template parameter. T was just meant to be used as any data type: int, float, double etc.. so T in my example above should never be int&. I did specifically state outside the realm of templates.

          – Francis Cugler
          yesterday





          I think there was a misconception on what I was trying to say; I'm using T here not as a template parameter. T was just meant to be used as any data type: int, float, double etc.. so T in my example above should never be int&. I did specifically state outside the realm of templates.

          – Francis Cugler
          yesterday













          Hmm, it sounds as if you are explaining that you can see this without templates, which is no problem. But then your final sentence claims to offer a solution to OP's problem, and that problem definitely involves templates. It's fine to offer a solution to a template question which is broader than just templates. But a solution to a template question that isn't accurate for templates, seems not to answer the question.

          – Ben Voigt
          yesterday







          Hmm, it sounds as if you are explaining that you can see this without templates, which is no problem. But then your final sentence claims to offer a solution to OP's problem, and that problem definitely involves templates. It's fine to offer a solution to a template question which is broader than just templates. But a solution to a template question that isn't accurate for templates, seems not to answer the question.

          – Ben Voigt
          yesterday















          This can also be an issue with no templates involved: using T = int&; void f(const T&); declares void f(int&);.

          – aschepler
          yesterday





          This can also be an issue with no templates involved: using T = int&; void f(const T&); declares void f(int&);.

          – aschepler
          yesterday













          @aschepler true, but I wasn't referring to using clauses; just basic function declarations in general.

          – Francis Cugler
          yesterday





          @aschepler true, but I wasn't referring to using clauses; just basic function declarations in general.

          – Francis Cugler
          yesterday


















          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%2f54480599%2fwhy-is-const-t-not-sure-to-be-const%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

          Paul Cézanne

          UIScrollView CustomStickyHeader Resize height generates problems when scroll is too fast

          Angular material date-picker (MatDatepicker) auto completes the date on focus out