Search code examples
c++11type-conversionstdatomic

Strict conversion of std::atomic_bool values


I have a code with very simple logical arithmetic that involves values returned from std::atomic_bool.

#include <iostream>
#include <atomic>

int main() {
    uint16_t v1 = 0x1122;
    uint16_t v2 = 0xaaff;
    std::atomic_bool flag1(false);

    uint16_t r1 = v1 | v2;
    std::cout << std::hex << r1 << std::endl;

    uint16_t r2 = static_cast<uint16_t>(flag1.load()) | static_cast<uint16_t>(0xaaff);
    std::cout << std::hex << r2 << std::endl;

    std::cout << __VERSION__ << std::endl;
}

Code example is here. Compile line: g++ -std=c++17 -O3 -Wall -pedantic -Wconversion -pthread main.cpp && ./a.out.

Based on the STD API, load() should return the underlined type stored in the atomic. So the flag1.load() should be returning bool. However, the compiler send a warning that it is asked to convert an int to uint16_t:

main.cpp:13:55: warning: conversion from 'int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Wconversion]
     uint16_t r2 = static_cast<uint16_t>(flag1.load()) | static_cast<uint16_t>(0xaaff);
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Where exactly does it do this conversion? Both sides of the | are converted to uint16_t. Why is it still printing a warning?


Solution

  • Usual arithmetic conversions (as specified at §5 of ISO standard) define that any operands for most or all arithmetic and binary operators undergo integer promotion before the operation itself.

    This means that both uint16_t operands are first promoted to int to compute the bitwise | and then truncated back to uint16_t to store in r2.

    Indeed that's what the warning is about: there's an implicit truncation of an int to an uint16_t.

    These conversions also define that a bool will always evaluate to 1 or 0, so the first cast is useless, but since the second operand will be promoted to an int, then also the second cast is useless, you could go with

    uint16_t r2 = flag.load() | 0xaaff;
    

    and possibly silence the warning by explicitly casting to a narrower type, which makes you aware of the fact that this is happening:

    uint16_t r2 = static_cast<uint16_t>(flag.load() | 0xaaff);