Home > Enterprise >  How to remove unused data-members?
How to remove unused data-members?

Time:10-31

The following code contains an example template X, where the data-member is unused, if the template is parametrized with other type than A. But the sizes of the objects a and b are the same, even with -O3, so the optimizer does not remove the unused data-member x2.

#include <iostream>
#include <cstdint>
#include <type_traits>

struct A {};
struct B {};

template<typename T>
struct X {
    int value() const {
        if constexpr(std::is_same_v<T, A>) {
            return x1   x2;
        }
        else {
            return x1;
        }
    }
private:
    int x1{0};    
    int x2{0};    
};

int main() {
    X<A> a;
    X<B> b;
    
    std::cout << sizeof(a) << '\n';
    std::cout << sizeof(b) << '\n';
    return a.value()   b.value();
}

Now there are two questions:

  1. Is the optimizer not allowed (as-if-rule) to remove the unused data-member? Why?
  2. How to achieve the goal: that the class X<B> does not contain the unused data-member x2?

There is a workaround with a base-class template and a specialisation for A that contains the data-member x2. But this solution is cumbersome. I wonder if there is a solution without using a base class?

Edit: I don't think that using the sizeof() operator prevents the optimization:

//#include <iostream>
#include <cstdint>
#include <type_traits>

struct A {};
struct B {};

template<typename T>
struct X {
    int value() const {
        if constexpr(std::is_same_v<T, A>) {
            return x1   x2;
        }
        else {
            return x1;
        }
    }
private:
    int x1{0};    
    int x2{1};    
};

X<A> a;
X<B> b;


int main() {
//    std::cout << sizeof(a) << '\n';
//    std::cout << sizeof(b) << '\n';
    return a.value()   b.value();
}

If you look a the assembly (e.g. compiler explorer) you see that the instances contain in both cases both data-members.

CodePudding user response:

Here is an ugly solution if x1 and x2 has the same type:

template<typename T>
struct X {
    int value() const {
        if constexpr(std::is_same_v<T, A>) {
            return x1()   x2();
        }
        else {
            return x1();
        }
    }

    int x1() const {
        return x[0];
    }

    int x2() const {
        return x[1];
    }

private:
    //int x1{0};    
    //int x2{0};
    int x[1   std::is_same_v<T, A>] = {};
};

CodePudding user response:

You can move the data members to a base class, and inherit conditionally.

namespace detail {

    struct XA {
        int x1{0};    
        int x2{0};    
    };
    
    struct XB {
        int x1{0};  
    };   
}

template<typename T>
struct X : private std::conditional_t<std::is_same_v<T, A>, detail::XA, detail::XB>{
    int value() const {
        if constexpr(std::is_same_v<T, A>) {
            return x1   x2;
        }
        else {
            return x1;
        }
    }   
};
  • Related