Об исключительной пользе explicit

Так уж повелось, что конструкторы в C++ по-умолчанию implicit, то есть поддерживают неявное преобразование, что порождает груду случайных ошибок. Приведу пример из жизни. Жили-были два класса математических векторов:

class vec2f {  
    float x;
    float y;
public:  
    vec2f(float x, float y);
    vec2f(const vec2f& other);
    // ...
};
class vec3f {  
    float x;
    float y;
    float z;
public:  
    vec3f(float x, float y, float z);
    vec3f(const vec3f& other);
    // ...
};

И всё у них было хорошо, пока кто-то не решил добавить в них "удобств", а именно, конструкторы из одного в другой:

vec2f::vec2f(const vec3f& other)  
: x(other.x), y(other.y) {}

vec3f::vec3f(const vec2f& other)  
: x(other.x), y(other.y), z(0.f) {}

Все обрадовались, как теперь всё стало хорошо, и начали этим добром пользоваться.
В другом государстве, при этом, жила функция camera_scale, которая о двумерном мире ничего не знала, да и знать не хотела:

void camera_scale(const vec3f& scale);  

И однажды весь мир этот исчез... Долго думали собравшись братцы, кто же и где же накосячил, пока в дебаге не обнаружили, что мир по Z-оси заскейлен в ноль. Догадливый читатель уже понял в чем дело:

vec2f v(1.f, 2.f);  
camera_scale(v);  

Вызывалась функция camera_scale с двумерным вектором (1;2), который неявно преобразовывался в вектор трехмерный (1;2;0) и мир весь при этом "сжимался" до нуля... Решение проблемы этой заключается в простом запрещении неявного преобразования, а именно в дописывании explicit нужным конструкторам:

explicit vec2f(const vec3f& other);  
explicit vec3f(const vec2f& other);  

Теперь на код, из сказки выше, компилятор будет ругаться нехорошими словами, а братцы, которые его писали, всегда объявляют конструкторы с одним параметром explicit, если явно не нужно обратного.

Проблему неявных преобразований признают тысячи разработчиков, но комитет не хочет пойти на несовместимость со старым кодом и запретить неявное преобразование по-умолчанию, приходится об этом всегда помнить.

Кого не убедила сказка выше, могу дать еще один кусок кода с этой проблемой:

struct String {  
    // преаллоцировать n знакомест
    String(size_t n) {
        ::printf("string(size_t n)");
    }
    // конструктор из raw-строки
    String(const char* str) {
        ::printf("string(const char* str)");
    }
};
void main() {  
    String str = 'n';
}

Угадайте, что выведет программа? ;) Именно! string(size_t n). Решение простое - дописать explicit для конструктора с параметром size_t, из const char* запрещать неявное преобразование не нужно, потому что тогда бы наш чудо-класс строки стал бы менее удобен.