C++ Core Guidelines: Better Specific or Generic?

Contents[Show]

Concepts revolutionise the way we think about and use generic programming. They didn't make it in C++11, or C++17 but with C++20 we will get them with high probability.

roads 320371 1280

Before I write about the use of concepts, I want to make a general remark.

Too Specific versus Too Generic

Until C++20 we have in C++ two diametral ways to think about functions or user-defined types (classes). Functions or classes can be defined on specific types or on generic types. In the second case, we call them function or class templates. What are the downsides of each way?

Too Specific

It's quite a job to define for each specific type a function or a class. To avoid that burden, type conversion comes often to our rescue but is also part of the problem. Let's see what I mean.

Narrowing Conversion

You have a function getInt(int a) which you can invoke with a double. Now, narrowing conversion takes places.

// narrowingConversion.cpp

#include <iostream>

void needInt(int i){
    std::cout << "int: " << i << std::endl;
}

int main(){
	
    std::cout << std::endl;
	
	double d{1.234};
	std::cout << "double: " << d << std::endl;
	
	needInt(d);
	
	std::cout << std::endl;
	
}

 

I assume that is not the behaviour you wanted. You started with a double and ended with an int.

narrowingConversion

But conversion works also the other way around.

Integral Promotion

You have a user-defined type MyHouse. An instance of MyHouse can be constructed in two ways. When invoked without an argument (1), it's attribute family is set to an empty string. This means the house is still empty.  To easily check if the house is empty or full, I implemented a conversion operator to bool (2). Fine or? No!

// conversionOperator.cpp

#include <iostream>
#include <string>

struct MyHouse{
    MyHouse() = default;                            // (1)
    MyHouse(const std::string& fam): family(fam){}
	
    operator bool(){ return !family.empty(); }      // (2)                         
	
    std::string family = "";
};

void needInt(int i){
    std::cout << "int: " << i << std::endl;
}

int main(){
	
    std::cout << std::boolalpha << std::endl;
	
    MyHouse firstHouse;
    if (!firstHouse){                                        
        std::cout << "The firstHouse is still empty." << std::endl;
    };
	
    MyHouse secondHouse("grimm");                               
    if (secondHouse){
        std::cout << "Family grimm lives in secondHouse." << std::endl;
    }
	
    std::cout << std::endl;
	
    needInt(firstHouse);              // (3)                
    needInt(secondHouse);             // (3)
	
    std::cout << std::endl;
	
}

 

Now, instances of MyHouse can be used, when an int is required. Strange! 

conversionOperator

Due to the overloaded operator bool (2), instances of MyHouse can be used as an int and can, therefore, be used in arithmetic expressions: auto res = MyHouse() + 5. This was not my intention! Just for completeness. With C++11 you can declare conversion operators as explicit. Therefore implicit conversions are not allowed.

My strong belief is that we need for convenience reasons the entire magic of conversions in C/C++ to deal with the fact that functions only accept specific arguments.

Are templates the cure? No!

Too Generic

Generic functions or classes can be invoked with arbitrary values. If the values do not satisfy the requirements of the function or class, no problem. You will get a compile time error. Fine!

// gcd.cpp

#include <iostream>

template<typename T>
T gcd(T a, T b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

int main(){

  std::cout << std::endl;

  std::cout << gcd(100, 10)  << std::endl;
 
  std::cout << gcd(3.5, 4.0)<< std::endl;
  std::cout << gcd("100", "10") << std::endl;

  std::cout << std::endl;

}

 

What is the problem with this error message?

 genericGCD

Of course, it is quite long and quite difficult to understand but my crucial concern is a different one. The compilation fails because neither double nor the C-strings supports the % operator. This means the error is due to the failed template instantiation for double and C-string. This is too late and, therefore, really bad. No template instantiation for type double or C-strings should be possible. The requirements for the arguments should be part of the function declaration and not a side-effect of an erroneous template instantiation.

Now concepts come to our rescue.

The Third Way

With concepts, we get something in between. With them, we can define functions or classes which act on semantic categories. Meaning the arguments of functions or classes are either too specific nor too generic but named sets of requirements such as Integral.

What's next?

Sorry for this short post but one week before my multithreading workshop at the CppCon I had neither the time nor in particular the resources (no connectivity in the national parks in Washington state) to write a full post. My next post will be special because I will write about the CppCon. Afterwards, I continue my story about generics and in particular about concepts.

 

 

 

Thanks a lot to my Patreon Supporters: Eric Pederson, Paul Baxter,  Meeting C++, Matt Braun, Avi Lachmish, Roman Postanciuc, Venkata Ramesh Gudpati, Tobias Zindl, Mielo, and Dilettant.

 

Get your e-book at Leanpub:

The C++ Standard Library

 

Concurrency With Modern C++

 

Get Both as one Bundle

cover   ConcurrencyCoverFrame   bundle
With C++11, C++14, and C++17 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages.  

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.

I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more the 100 source files.

 

Get my books "The C++ Standard Library" (including C++17) and "Concurrency with Modern C++" in a bundle.

In sum, you get more than 600 pages full of modern C++ and more than 100 source files presenting concurrency in practice.

 

 Get your interactive course at educative

Modern C++ Concurrency in Practice: Get the most out of any machine

educative

Based on my book "Concurrency with Modern C++" educative.io created an interactive course.

What's Inside?

  • 140 lessons
  • 110 code playgrounds => Run in browser
  • 78 code snippets
  • 55 illustrations

Add comment


Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 157

All 1406345

Currently are 151 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments