c++structtype-traitsmember-variablesscope-resolution-operator

calling version of is_const<> but for variables instead of types, in one line


Hi I am learning c++ and I read about type traits such as is_const. is_const can be called in one line like,

cout << is_const<double>::value << endl;

I made my own version of is_const but to test if a variable is const, and it can be used like this,

#include<iostream>
using namespace std;

template<typename T>
  struct check_const {
    check_const(const T *x): val(std::true_type{})
    { }
    check_const(T *x) : val(std::false_type{})
    { }
    bool val;
};

int main() 
{
   const double pi= 3.14;
   check_const<double> r(&pi);
   cout <<  r.val << endl;    // returns 1
   double x= 2.7;
   check_const<double> s(&x);
   cout << s.val << endl;    // returns 0
   return(0);
}

I would like to call check_const in one line as well, but the compiler keeps giving me errors like

"typename not allowed" 

when I try calling it like

cout << check_const<double> t(&pi)::val << endl;

How can I change check_const, so it can be called in one line?


Solution

  • You are just using a slightly wrong syntax here:

    cout << check_const<double> t(&pi)::val << endl;
    

    Instead use

    cout << check_const<double>(&pi).val << endl; 
    

    check_const<double> t(&pi) is the syntax for the definition of a named variable, but you cannot have declarations/definitions inside an expression.

    check_const<double>(&pi) is the syntax to create an unnamed temporary, which can be done in expressions.

    Then you need . instead of ::, because val is a non-static member of check_const.

    Starting with C++17 you could also write:

    cout << check_const(&pi).val << endl; 
    

    and have the template argument be deduced for you.


    All of this can be simplified though, as you aren't really using the class. You can just use the constructors as free functions:

    template<typename T>
    constexpr bool check_const(const T *x) noexcept { return true; }
    
    template<typename T>
    constexpr bool check_const(T *x) noexcept { return false; }
    

    (The constexpr makes it possible to use the functions in constant expressions, but is otherwise not required. Similarly noexcept is just an indicator that the function doesn't throw exceptions, but is otherwise not required.)

    This can be used more easily as

    cout << check_const(&pi) << endl;
    

    Also, instead of pointers, use references:

    template<typename T>
    constexpr bool check_const(const T &x) noexcept { return true; }
    
    template<typename T>
    constexpr bool check_const(T &x) noexcept { return false; }
    

    and you can write

    cout << check_const(pi) << endl;